import subprocess
import os
import threading
import time
from gpiozero import LED, PWMLED
# Use Mock pins for non-Pi testing if needed
try:
    from gpiozero.pins.mock import MockPin 
except ImportError:
    pass

# --- Configuration ---
LED_PIN_1 = 12 
LED_PIN_2 = 24
LED_PIN_3 = 18 # Must be a PWM capable pin (most are)
LED_PIN_4 = 16
NEW_PIN_1 = 20 # New pin definition
NEW_PIN_2 = 21 # New pin definition

PHASE1_DURATION = 31.4# Duration for each phase in seconds
PHASE2_DURATION = 27.75 # Duration for each phase in seconds
PHASE3_DURATION = 5.5  # Duration for this phase (solid LEDs 1-4, toggling new pins)
PHASE3_75_DURATION = 13.3 # New duration for the added phase (solid LED 4)
PHASE4_DURATION = 8.3  # Duration for the original strobe phase in seconds
PHASE5_DURATION = 17.96 #Duration for the new final phase (solid LED 4)
SILENT_PHASE_DURATION = 4.25 # New duration for the silent delay phase
SOUND_FILE_PATH = "/home/dj/Desktop/intrusion-detector/sound.mp3"

# Initialize LEDs and new pins
try:
    led1 = LED(LED_PIN_1)
    led2 = LED(LED_PIN_2)
    led3 = PWMLED(LED_PIN_3) 
    led4 = LED(LED_PIN_4)
    new_led1 = LED(NEW_PIN_1)
    new_led2 = LED(NEW_PIN_2)
except Exception:
    print("GPIO pins not available. Using mock pins for simulation.")
    led1 = LED(MockPin(LED_PIN_1))
    led2 = LED(MockPin(LED_PIN_2))
    led3 = PWMLED(MockPin(LED_PIN_3)) 
    led4 = LED(MockPin(LED_PIN_4))
    new_led1 = LED(MockPin(NEW_PIN_1))
    new_led2 = LED(MockPin(NEW_PIN_2))

# A global event to signal threads to stop cleanly
stop_event = threading.Event()

# --- Audio Playback Function ---

def play_alert_sound(sound_file_path):
    """
    Plays the specified sound file using ffplay in a loop 
    until the main stop_event is set.
    """
    print(f"Starting audio playback of {sound_file_path}")
    command = ["ffplay", "-nodisp", "-autoexit", "-loop", "0", sound_file_path]
    
    process = None
    try:
        process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        
        while not stop_event.is_set():
            time.sleep(1) 
            if process.poll() is not None:
                print("ffplay process terminated unexpectedly. Exiting audio thread.")
                break
    except FileNotFoundError:
        print(f"Error: 'ffplay' command not found or sound file path is incorrect.")
        print("Please ensure ffplay is installed and in your system's PATH.")
    except Exception as e:
        print(f"An error occurred during audio playback: {e}")



# --- Visual Alert Functions ---

def flash_alternative_leds():
    print("Phase 1: Flashing LEDs 1 and 2 alternatively.")
    while not stop_event.is_set():
        led1.on()
        led2.off()
        if stop_event.wait(0.15): break 
        led1.off()
        led2.on()
        if stop_event.wait(0.15): break
    led1.off() 
    led2.off()
    print("Phase 1 complete. LEDs off.")

def fade_led_cycle():
    print("Phase 2: Fading LED 3.")
    while not stop_event.is_set():
        for value in range(3, 101, 2): 
            if stop_event.is_set(): break
            led3.value = value / 100.0
            time.sleep(0.04) 
        for value in range(100, 2, -2):
            if stop_event.is_set(): break
            led3.value = value / 100.0
            time.sleep(0.04) 
    led3.value = 0
    print("Phase 2 complete. LED off.")

def toggle_new_pins_and_solid_leds():
    """
    Toggles new pins (20 and 21) simultaneously on for 2s, off for 2s,
    while keeping LEDs 1 through 4 solid ON for the duration of the phase.
    """
    print("Phase 3: LEDs 1, 2, 3, 4 solid ON, while Pins 20 and 21 toggle (2s on/off cycle).")
    
    # Turn main LEDs ON immediately
    led1.on()
    led2.on()
    led3.on()
    led4.on()

    # Toggle new pins in a loop until the main stop_event is set by the main thread
    while not stop_event.is_set():
        new_led1.on()
        new_led2.on()
        # Use wait to allow graceful interruption
        if stop_event.wait(2.0): break 
        new_led1.off()
        new_led2.off()
        if stop_event.wait(2.0): break
        
    # Ensure all LEDs are turned off when the loop breaks
    led1.off()
    led2.off()
    led3.off()
    led4.off()
    new_led1.off()
    new_led2.off()
    print("Phase 3 complete. All involved LEDs off.")


def strobe_led():
    print("Phase 4: Strobing LED 4.")
    while not stop_event.is_set():
        led4.on()
        if stop_event.wait(0.1): break 
        led4.off()
        if stop_event.wait(0.1): break
    led4.off()
    print("Phase 4 complete. LED off.")
    
def solid_led4_on():
    """
    Turns LED 4 on solid until the stop event is set.
    Used for Phase 3.75 and Phase 5.
    """
    print("Phase (Solid LED 4): LED 4 solid on.")
    led4.on()
    stop_event.wait()
    led4.off()
    print("Solid LED 4 Phase complete. LED off.")


# --- Main function to manage the threads sequentially ---
def main():
    print("Script started.")
    
    audio_thread = threading.Thread(target=play_alert_sound, args=(SOUND_FILE_PATH,))
    audio_thread.start()

    try:
        # --- Phase 1 ---
        stop_event.clear()
        thread1 = threading.Thread(target=flash_alternative_leds)
        thread1.start()
        time.sleep(PHASE1_DURATION) 
        stop_event.set()
        thread1.join()

        # --- Phase 2 ---
        stop_event.clear() 
        thread2 = threading.Thread(target=fade_led_cycle) 
        thread2.start()
        time.sleep(PHASE2_DURATION)
        stop_event.set()
        thread2.join()

        # --- SILENT PHASE (Phase 2.5) ---
        print(f"Starting Silent Phase: All pins off for {SILENT_PHASE_DURATION} seconds. Music continues.")
        time.sleep(SILENT_PHASE_DURATION)
        print("Silent Phase complete.")

        # --- Phase 3 (Modified) ---
        stop_event.clear()
        thread3 = threading.Thread(target=toggle_new_pins_and_solid_leds)
        thread3.start()
        time.sleep(PHASE3_DURATION)
        stop_event.set()
        thread3.join()
        
        # --- NEW PHASE 3.75 (Identical to Phase 5: Solid LED 4) ---
        stop_event.clear()
        # Reusing the solid_led4_on function
        thread3_75 = threading.Thread(target=solid_led4_on) 
        thread3_75.start()
        time.sleep(PHASE3_75_DURATION) # Use the new duration variable
        stop_event.set()
        thread3_75.join()
        
        # --- Phase 4 ---
        stop_event.clear()
        thread4 = threading.Thread(target=strobe_led)
        thread4.start()
        time.sleep(PHASE4_DURATION)
        stop_event.set()
        thread4.join()
        
        # --- Phase 5 (NEW) ---
        stop_event.clear()
        thread5 = threading.Thread(target=solid_led4_on)
        thread5.start()
        time.sleep(PHASE5_DURATION)
        stop_event.set()
        thread5.join()

        print("All visual phases complete.")

    except KeyboardInterrupt:
        print("Program interrupted by user.")
        
    finally:
        # Signal ALL threads (visual and audio) to stop
        stop_event.set() 
        # Wait for the audio thread to finish its cleanup
        audio_thread.join() 

        # Cleanup GPIO pins
        led1.off()
        led2.off()
        led3.off()
        led4.off()
        new_led1.off()
        new_led2.off()
        print("Cleanup complete. Exiting.")

if __name__ == "__main__":
    main()

