#!/usr/bin/env python3
import time
import sys
import os
import signal
import importlib
import subprocess
from datetime import datetime
from babicli.colors import GREEN, RED, RESET

# ===============================================================
# CONFIG
# ===============================================================
ADMIN_CODE = "28021986"
LAUNCH_CODE_LENGTH = 6
LOG_DIR = "launchlogs"

if not os.path.exists(LOG_DIR):
    os.makedirs(LOG_DIR)

log_file = None


# ===============================================================
# AUTO-INSTALL UTILITY
# ===============================================================
def ensure(pkg):
    try:
        importlib.import_module(pkg)
        return True
    except ImportError:
        ans = input(f"Package '{pkg}' is required. Install it? [Y/n] ").strip().lower()
        if ans in ["", "y", "yes"]:
            try:
                subprocess.check_call([sys.executable, "-m", "pip", "install", pkg])
                return True
            except Exception:
                return False
        return False


# ===============================================================
# CTRL-C DISABLED
# ===============================================================
def block_ctrl_c(signum, frame):
    print("\nCTRL-C is disabled. Close the terminal window (X) to exit safely.")
signal.signal(signal.SIGINT, block_ctrl_c)


# ===============================================================
# SIM / GPIO MODE SETUP
# ===============================================================
SIM_REQUESTED = (len(sys.argv) > 2 and sys.argv[2].lower() == "sim")
SIM = SIM_REQUESTED

if not SIM:
    if ensure("RPi.GPIO"):
        try:
            import RPi.GPIO as GPIO
        except Exception:
            SIM = True
    else:
        SIM = True

system_wants = {"k1": None, "k2": None, "launch": None}

if SIM:
    print("SIMULATION MODE (1=KEY1, 2=KEY2, 3=LAUNCH)")
    ensure("keyboard")
    import keyboard

    class FakeGPIO:
        BCM = IN = OUT = PUD_DOWN = None
        pins = {}
        def setmode(self, *a, **k): pass
        def setup(self, pin, mode, pull_up_down=None): self.pins[pin]=0
        def input(self, pin): return self.pins.get(pin,0)
        def output(self, pin,val): self.pins[pin]=val
        def cleanup(self): pass

    GPIO = FakeGPIO()

    def apply_state_change(key):
        if system_wants[key] is None: return
        if key == "k1": GPIO.pins[17] = int(system_wants[key])
        if key == "k2": GPIO.pins[27] = int(system_wants[key])
        if key == "launch":
            GPIO.pins[22] = 1; time.sleep(0.15); GPIO.pins[22]=0
        system_wants[key] = None

    keyboard.on_press_key("1", lambda e: apply_state_change("k1"))
    keyboard.on_press_key("2", lambda e: apply_state_change("k2"))
    keyboard.on_press_key("3", lambda e: apply_state_change("launch"))


# ===============================================================
# PINS
# ===============================================================
KEY1 = 17
KEY2 = 27
LAUNCH = 22
LED_RED = 5
LED_GREEN = 6
LAUNCH_OUT = 13

ARM_WINDOW = 0.5
BAR_LENGTH = 29
LAUNCH_WINDOW_SECONDS = 30


# ===============================================================
# LOGGING
# ===============================================================
def log(msg):
    global log_file
    ts = datetime.now().strftime("%H:%M:%S")
    line = f"[{ts}] {msg}\n"
    print(line, end="")
    if log_file:
        log_file.write(line)
        log_file.flush()


# ===============================================================
# AUTHORIZATION
# ===============================================================
import random

def require_authorization():
    global log_file

    code = "".join(random.choice("0123456789") for _ in range(LAUNCH_CODE_LENGTH))
    print(f"ENTER LAUNCH CODE TO BEGIN: {code}")
    entered = input("Code: ").strip()

    if entered == ADMIN_CODE:
        path = os.path.join(LOG_DIR, f"ADMIN_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt")
        log_file = open(path, "w")
        log("ADMIN OVERRIDE USED")
        print(f"{GREEN}Admin override accepted.{RESET}")

    elif entered == code:
        path = os.path.join(LOG_DIR, f"{code}.txt")
        log_file = open(path, "w")
        log(f"Launch code {code} accepted.")

    else:
        print(f"{RED}INVALID CODE. EXITING.{RESET}")
        if "GPIO" in globals():
            GPIO.cleanup()
        sys.exit(1)

    name = input("Enter operator name: ").strip()
    ID = input("Enter operator ID number: ").strip()

    log(f"Operator: {name}")
    log(f"Operator ID: {ID}")

    input("\nPress ENTER to continue...\n")


# ===============================================================
# BAR
# ===============================================================
def countdown_bar(total):
    for remaining in range(total, 0, -1):
        filled = int((remaining / total) * BAR_LENGTH)
        bar = RED + ("█" * filled + "░" * (BAR_LENGTH - filled)) + RESET
        sys.stdout.write(f"\r[{bar}] {remaining}s")
        sys.stdout.flush()
        yield remaining
        time.sleep(1)
    print()


# ===============================================================
# MAIN
# ===============================================================
def run():

    require_authorization()

    # SAFE GPIO INIT
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(KEY1, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
    GPIO.setup(KEY2, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
    GPIO.setup(LAUNCH, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
    GPIO.setup(LED_RED, GPIO.OUT)
    GPIO.setup(LED_GREEN, GPIO.OUT)
    GPIO.setup(LAUNCH_OUT, GPIO.OUT)

    GPIO.output(LED_RED, 0)
    GPIO.output(LED_GREEN, 0)
    GPIO.output(LAUNCH_OUT, 0)

    # PRE-LAUNCH CONFIRM
    print("Press the LAUNCH button once to confirm starting the launch sequence.")
    log("Waiting for pre-launch button confirmation...")

    while not GPIO.input(LAUNCH): time.sleep(0.05)
    while GPIO.input(LAUNCH): time.sleep(0.05)

    print(f"{GREEN}Launch-button confirmation received.{RESET}")
    log("Launch-button confirmation received.")

    # ARM SEQUENCE
    print("Turn both keys to ARM...")
    log("Waiting for keys to ARM...")
    system_wants["k1"] = True
    system_wants["k2"] = True

    while not GPIO.input(KEY1) and not GPIO.input(KEY2):
        time.sleep(0.05)

    armed = False
    start = time.time()
    while time.time() - start < ARM_WINDOW:
        if GPIO.input(KEY1) and GPIO.input(KEY2):
            armed = True
            break
        time.sleep(0.01)

    if not armed:
        log("ARM FAILED")
        GPIO.cleanup()
        return

    print(f"{RED}ARMED.{RESET}")
    log("System ARMED.")
    GPIO.output(LED_RED, 1)

    print("Press launch button within 30 seconds.")
    log("Launch window started.")

    for remaining in countdown_bar(LAUNCH_WINDOW_SECONDS):

        if not GPIO.input(KEY1) or not GPIO.input(KEY2):
            print("\nABORT: A key was turned OFF.")
            log("ABORT: Key turned off.")
            GPIO.cleanup()
            return

        if GPIO.input(LAUNCH):
            print(f"\n{GREEN}LAUNCH CONFIRMED{RESET}")
            log("Launch button pressed.")
            GPIO.output(LED_GREEN, 1)
            GPIO.output(LAUNCH_OUT, 1)
            print("FIRING COMPLETE")
            log("Launch COMPLETED.")
            GPIO.cleanup()
            return

    print("\nLaunch window expired.")
    log("Launch FAILED: Timeout.")
    GPIO.cleanup()
    return


# ===============================================================
# SAFE EXIT
# ===============================================================
try:
    run()
finally:
    if log_file:
        log_file.write("\n--- END OF LOG ---\n")
        log_file.close()
