import board
import touchio
import time
import neopixel
import math
from adafruit_led_animation.color import ORANGE, BLUE, WHITE, AMBER, BLACK
from adafruit_led_animation.animation.comet import Comet
from adafruit_led_animation.animation.pulse import Pulse
from adafruit_led_animation.animation.rainbow import Rainbow
from adafruit_led_animation.animation.rainbowcomet import RainbowComet
from adafruit_led_animation.animation.rainbowsparkle import RainbowSparkle
from adafruit_led_animation.animation.sparklepulse import SparklePulse
from adafruit_led_animation.sequence import AnimationSequence

from adafruit_ble import BLERadio
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from adafruit_ble.services.nordic import UARTService
from adafruit_bluefruit_connect.packet import Packet
from adafruit_bluefruit_connect.color_packet import ColorPacket
from adafruit_bluefruit_connect.button_packet import ButtonPacket

# Initialize NeoPixels
pixels = neopixel.NeoPixel(board.NEOPIXEL, 10, brightness=0.2, auto_write=False)
strip = neopixel.NeoPixel(board.A1, 20, brightness=0.5, auto_write=False)
pixels.fill(ORANGE)
strip.fill(ORANGE)
pixels.show()
strip.show()

touchpad = touchio.TouchIn(board.A5)

# BLE Setup
ble = BLERadio()
uart_server = UARTService()
advertisement = ProvideServicesAdvertisement(uart_server)
advertisement.complete_name = "Minjanboard"
ble.name = advertisement.complete_name
ble_active = False
run_ble_animation = False
animation_number = -1
current_animation = None
pickedColor = AMBER
adjustedTime = 0.1
cometTailLength = int(20 / 3) + 1  # For 20 LEDs on strip

# Timing constants
HOLD_THRESHOLD = 0.5
TAP_THRESHOLD = 0.4
DEBOUNCE = 0.1
HOLD_INCREMENT = 0.02
MIN_BRIGHTNESS = 0.1

# State variables
leds_on = True
current_brightness = max(0.2, MIN_BRIGHTNESS)
brightness_direction = 1
last_tap_time = 0
tap_count = 0
animation_mode = "idle"
pulse_phase = 0.0
flash_step = 0

print("Gesture controller ready!")


def lazy_pulse():
    global pulse_phase
    brightness = (math.sin(pulse_phase) * 0.1 + 0.5) * current_brightness
    pixels.brightness = brightness
    strip.brightness = brightness
    pulse_phase += 0.05
    if pulse_phase >= 2 * math.pi:
        pulse_phase = 0.0
    pixels.show()
    strip.show()


def configureRainbowAnimation():
    return AnimationSequence(
        Rainbow(strip, speed=adjustedTime, period=2),
        RainbowComet(strip, speed=adjustedTime, tail_length=cometTailLength, bounce=True),
        RainbowSparkle(strip, speed=adjustedTime, period=5, num_sparkles=int(20 / 3)),
        advance_interval=5,
        auto_clear=True
    )


def runSelectedAnimation():
    global first_time
    global current_animation
    global pixels
    pixels.fill(WHITE)
    if first_time:
        strip.fill(BLACK)
        strip.show()
    if animation_number == 1:
        if first_time:
            print("*** COMET or LARSON SCANNER ***")
            print(f"adjustedTime: {adjustedTime}, pickedColor: {pickedColor}")
            current_animation = Comet(strip, speed=adjustedTime, color=pickedColor, tail_length=cometTailLength,
                                      bounce=False)
            first_time = False
        current_animation.animate()
    elif animation_number == 2:
        if first_time:
            print("*** PULSE ***")
            print(f"adjustedTime: {adjustedTime}, pickedColor: {pickedColor}")
            current_animation = Pulse(strip, speed=adjustedTime, color=pickedColor, period=3)
            first_time = False
        current_animation.animate()
    elif animation_number == 3:
        if first_time:
            print("*** SPARKLE PULSE ***")
            print(f"adjustedTime: {adjustedTime}, pickedColor: {pickedColor}")
            current_animation = SparklePulse(strip, speed=adjustedTime, period=12, color=pickedColor)
            first_time = False
        current_animation.animate()
    elif animation_number == 4:
        if first_time:
            rainbowSequence = configureRainbowAnimation()
            print("*** RAINBOW ***")
            pixels.fill(BLACK)
            pixels.show()  # Add this line to actually turn off the pixels
            current_animation = rainbowSequence
            first_time = False
        current_animation.animate()

ble.stop_advertising()
while True:
    current_time = time.monotonic()

    # Handle local animations if BLE not connected
    if leds_on and not (ble_active and ble.connected):
        if animation_mode == "pulse":
            lazy_pulse()

    # Touch handling
    if touchpad.value and not (ble_active and ble.connected):
        touch_start = current_time
        hold_detected = False
        while touchpad.value:
            if time.monotonic() - touch_start > HOLD_THRESHOLD:
                hold_detected = True
                last_adjust = time.monotonic()

                while touchpad.value:
                    if time.monotonic() - last_adjust > 0.05:
                        current_brightness += HOLD_INCREMENT * brightness_direction
                        current_brightness = max(MIN_BRIGHTNESS, min(1.0, current_brightness))
                        if leds_on and animation_mode == "idle":
                            pixels.brightness = current_brightness
                            strip.brightness = current_brightness
                            pixels.show()
                            strip.show()
                        last_adjust = time.monotonic()
                brightness_direction *= -1
                break
            time.sleep(0.01)

        if not hold_detected:
            if current_time - last_tap_time < TAP_THRESHOLD:
                tap_count += 1
            else:
                tap_count = 1
            last_tap_time = current_time

            while touchpad.value:
                pass
            time.sleep(DEBOUNCE)

    # Process taps
    if current_time - last_tap_time > TAP_THRESHOLD and tap_count > 0:
        if tap_count == 1:
            leds_on = not leds_on
            if leds_on:
                pixels.brightness = current_brightness
                strip.brightness = current_brightness 
                animation_mode = "idle"
                pixels.fill(ORANGE)
                strip.fill(ORANGE)
            else:
                pixels.brightness = 0
                strip.brightness = 0
            pixels.show()
            strip.show()
        elif tap_count == 2:
            if leds_on:
                animation_mode = "pulse"
                pixels.fill(ORANGE)
                strip.fill(ORANGE)
                pixels.show()
                strip.show()
        elif tap_count >= 3:
            ble_active = True
            pixels.fill(BLUE)
            strip.fill(BLUE)
            pixels.show()
            strip.show()
            time.sleep(1)
            pixels.fill(ORANGE)
            strip.fill(ORANGE)
            pixels.show()
            strip.show()
        tap_count = 0

    # BLE Handling
    if ble_active:
        if not ble.connected:
            if not ble.advertising:
                ble.start_advertising(advertisement)
                print(f"BLE Advertising of {ble.name} started")
        else:
            #print("are we at least here")
            if uart_server.in_waiting:
                #print("wat does this do")
                try:
                    #print("what the fuck")
                    packet = Packet.from_stream(uart_server)
                    if isinstance(packet, ColorPacket):
                        pickedColor = packet.color
                        pixels.fill(pickedColor)
                        strip.fill(pickedColor)
                        strip.show()
                        pixels.show()
                        run_ble_animation = False
                        animation_number = 0
                    elif isinstance(packet, ButtonPacket) and packet.pressed:
                        if packet.pressed:
                            if packet.button == ButtonPacket.BUTTON_1:  # app button 1 pressed
                                first_time = True
                                animation_number = 1
                                runAnimation = True
                            elif packet.button == ButtonPacket.BUTTON_2:  # app button 2 pressed
                                first_time = True
                                animation_number = 2
                                runAnimation = True
                            elif packet.button == ButtonPacket.BUTTON_3:  # app button 3 pressed
                                first_time = True
                                animation_number = 3
                                runAnimation = True
                            elif packet.button == ButtonPacket.BUTTON_4:
                                pixels.fill(BLACK)
                                pixels.brightness = 0
                                pixels.show()  # Add this line to immediately update the pixels
                                first_time = True
                                animation_number = 4
                                runAnimation = True
                        run_ble_animation = True
                except ValueError:
                    print("fuck me")
                    pass
            if run_ble_animation:
                runSelectedAnimation()

    time.sleep(0.01)