import board
import neopixel
import touchio
import time
import sys
import supervisor  # gives us non-blocking stdin check

pixels = neopixel.NeoPixel(board.NEOPIXEL, 4, brightness=0.35, auto_write=False)
touch_a = touchio.TouchIn(board.TOUCH1)
touch_b = touchio.TouchIn(board.TOUCH2)

BLUE  = (0,   80, 255)
GREEN = (0,  255,  60)
RED   = (255,  0,   0)
OFF   = (0,    0,   0)

def show(colors):
    for i, c in enumerate(colors):
        pixels[i] = c
    pixels.show()

def spin_step(frame):
    head = frame % 4
    buf = [(int(BLUE[0]*0.15), int(BLUE[1]*0.15), int(BLUE[2]*0.15))] * 4
    buf[head] = BLUE
    buf[(head - 1) % 4] = (int(BLUE[0]*0.5), int(BLUE[1]*0.5), int(BLUE[2]*0.5))
    show(buf)

def success_animation():
    for _ in range(3):
        for brightness in range(0, 256, 12):
            c = (0, brightness, int(brightness * 0.25))
            show([c, c, c, c])
            time.sleep(0.01)
        for brightness in range(255, 0, -12):
            c = (0, brightness, int(brightness * 0.25))
            show([c, c, c, c])
            time.sleep(0.01)
    for brightness in range(0, 256, 12):
            c = (0, brightness, int(brightness * 0.25))
            show([c, c, c, c])
            time.sleep(0.01)
    for brightness in range(255, 0, -2):
            c = (0, brightness, int(brightness * 0.25))
            show([c, c, c, c])
            time.sleep(0.01)

def fail_animation():
    for _ in range(4):
        show([RED, RED, RED, RED])
        time.sleep(0.12)
        show([OFF, OFF, OFF, OFF])
        time.sleep(0.08)
    time.sleep(0.3)

def read_line_nonblocking():
    """Return a stripped line if one is available, else None."""
    if supervisor.runtime.serial_bytes_available:
        return sys.stdin.readline().strip()
    return None

# ── states ────────────────────────────────────────────────
IDLE    = 0
WAITING = 1
SUCCESS = 2
FAIL    = 3

state = IDLE
frame = 0
show([OFF, OFF, OFF, OFF])

while True:
    line = read_line_nonblocking()

    if line == "WAIT":
        state = WAITING
        frame = 0

    elif line == "OK":
        state = SUCCESS
        success_animation()
        show([OFF, OFF, OFF, OFF])
        state = IDLE

    elif line == "FAIL":
        state = FAIL
        fail_animation()
        show([OFF, OFF, OFF, OFF])
        state = IDLE

    if state == WAITING:
        spin_step(frame)
        frame += 1
        if touch_a.value and touch_b.value:
            print("TOUCHED")
            # wait for release before allowing another trigger
            while touch_a.value or touch_b.value:
                spin_step(frame)
                frame += 1
                time.sleep(0.07)
        time.sleep(0.07)
    elif state == IDLE:
        time.sleep(0.05)

