######
# LEAVE THESE IMPORTS!
######
from arduinIO import ArduinoController
import functions
import time

######
# config values
######

SERIAL_PORT = '/dev/ttyUSB0'
BAUD_RATE = 9600

######
# arduinIO values
######
ARDIO_PORT = "/dev/ttyACM2"
ARDIO_BAUDRATE = 115200
ARDIO_INPUT_PIN = 2
ARDIO_OUTPUT_PINS = [8, 9, 10, 11]  # ok, space, dot, dash
ARDIO_PULSE_DURATION_MS = 300

arduino = ArduinoController(port=ARDIO_PORT, baudrate=ARDIO_BAUDRATE)
arduino.connect()

###
# name, enabled, string to match
###
conditions = [
    ['rdy', False, "", 'setup_buttons'],
    ['ok', False, "", 'button_ok'],
    ['dash', False, "", 'button_dash'],
    ['spce', False, "", 'button_space'],
    ['dot', False, "", 'button_dot'],
    ['check', False, "", 'echo_trigger_state'],
    ['run', False, "", 'find_code'],
    ["Flag", True, "TS{", "stop_glitch"],
]

######
# Custom functions
######
def setup_buttons():
    version = arduino.get_version()
    functions.add_text(f"[INFO] Connected to Arduino: {version}")

    # Configure pins
    functions.add_text("[INFO] Configuring pin modes...")
    arduino.set_mode(ARDIO_INPUT_PIN, "INPUT")
    for pin in ARDIO_OUTPUT_PINS:
        arduino.set_mode(pin, "OUTPUT")
        arduino.set_default(pin, "LOW")

    # Display current configuration
    pinmap = arduino.get_pinmap()
    functions.add_text(f"[INFO] Pin map: {pinmap}")


def button_ok():
    # Pulse one output pin
    functions.add_text(f"[INFO] Pulsing output pin 8 for {ARDIO_PULSE_DURATION_MS} ms...")
    arduino.set_for(8, "HIGH", ARDIO_PULSE_DURATION_MS)
    time.sleep(0.5)


def button_dash():
    functions.add_text(f"[INFO] Pulsing output pin 11 for {ARDIO_PULSE_DURATION_MS} ms...")
    arduino.set_for(11, "HIGH", ARDIO_PULSE_DURATION_MS)
    time.sleep(0.5)


def button_space():
    functions.add_text(f"[INFO] Pulsing output pin 9 for {ARDIO_PULSE_DURATION_MS} ms...")
    arduino.set_for(9, "HIGH", ARDIO_PULSE_DURATION_MS)
    time.sleep(0.5)


def button_dot():
    functions.add_text(f"[INFO] Pulsing output pin 10 for {ARDIO_PULSE_DURATION_MS} ms...")
    arduino.set_for(10, "HIGH", ARDIO_PULSE_DURATION_MS)
    time.sleep(0.5)


def echo_trigger_state():
    state = arduino.get_state(ARDIO_INPUT_PIN)
    functions.add_text(f"[INFO] Input pin {ARDIO_INPUT_PIN} is currently {state}")

def find_code():
    candidate_pins = [8, 9, 10, 11]  # include pin 8 as a candidate
    code_sequence = []
    pin_to_symbol = {8: "OK", 9: "*", 10: ".", 11: "-"}

    functions.add_text("[INFO] Starting full code discovery sequence")

    for digit_index in range(4):
        functions.add_text(f"[INFO] Finding digit {digit_index + 1}")
        results = {}

        for test_pin in candidate_pins:
            # Build the sequence: previously found digits + candidate repeated to fill 4 pulses
            sequence = code_sequence.copy()
            remaining_pulses = 4 - len(sequence)
            sequence += [test_pin] * remaining_pulses
            symbol_seq = [pin_to_symbol.get(pin, str(pin)) for pin in sequence]
            functions.add_text(f"[TEST] Testing candidate pin {test_pin} ({pin_to_symbol.get(test_pin)}), "
                               f"sequence: {' '.join(symbol_seq)}")

            # Send all pulses in the sequence, starting timer immediately before the fourth pulse
            for i, pin in enumerate(sequence):
                if i == len(sequence) - 1:
                    # Start timer immediately before sending the fourth pulse
                    start_time = time.time()
                    arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
                    # Allow a short interval for the device to react
                    time.sleep(0.05)
                    # Wait for the input to go HIGH and capture result
                    result = arduino.wait_for(ARDIO_INPUT_PIN, "HIGH")
                    end_time = time.time()

                    # Safely extract duration from result or compute fallback
                    if isinstance(result, dict):
                        duration = result.get("duration_ms",
                                              int((end_time - start_time) * 1000))
                    else:
                        duration = int((end_time - start_time) * 1000)

                    functions.add_text(f"[RESULT] Candidate pin {test_pin} - LOW->HIGH {duration} ms.")
                    results[test_pin] = duration
                else:
                    arduino.set_for(pin, "HIGH", ARDIO_PULSE_DURATION_MS)
                    time.sleep(0.5)

            # small pause between candidates
            time.sleep(0.8)

        # Choose the candidate with the longest duration for this digit
        correct_pin = max(results, key=results.get)
        code_sequence.append(correct_pin)

        functions.add_text(f"[INFO] Digit {digit_index + 1} identified (pin): {correct_pin}")
        functions.add_text(f"[INFO] Digit {digit_index + 1} identified (symbol): "
                           f"{pin_to_symbol.get(correct_pin)}")

    translated_sequence = [pin_to_symbol.get(pin, str(pin)) for pin in code_sequence]
    functions.add_text(f"[INFO] Full code sequence identified (pins): {code_sequence}")
    functions.add_text(f"[INFO] Full code sequence identified (symbols): {translated_sequence}")

    return code_sequence, translated_sequence

def stop_glitch():
    functions.set_uart_switch(False)