import time
import lgpio
import atexit
import sys
from datetime import datetime
from config import PINS, FREQUENCY_HZ, DUTY_MODE

class SignalController:
    def __init__(self):
        self.chip = lgpio.gpiochip_open(0)
        lgpio.gpio_claim_output(self.chip, PINS["ON_OFF"])
        lgpio.gpio_claim_output(self.chip, PINS["DUTY"])
        lgpio.gpio_claim_output(self.chip, PINS["POLARITY"])

        # Initialize all signals to LOW
        lgpio.gpio_write(self.chip, PINS["ON_OFF"], 0)
        lgpio.gpio_write(self.chip, PINS["DUTY"], 0)
        lgpio.gpio_write(self.chip, PINS["POLARITY"], 0)

        # Register cleanup function to run on exit
        atexit.register(self.cleanup)
        
        # Store reference globally for emergency cleanup
        sys.modules[__name__].global_signal_controller = self
        
        # Log the duty mode being used
        timestamp = datetime.now().strftime("%H:%M:%S")
        if DUTY_MODE == "FULL":
            print(f"[{timestamp}] [SIGNAL] Duty mode: {DUTY_MODE} (POLARITY: {FREQUENCY_HZ}Hz, DUTY: HIGH)")
        else:
            duty_freq = FREQUENCY_HZ * 2
            print(f"[{timestamp}] [SIGNAL] Duty mode: {DUTY_MODE} (POLARITY: {FREQUENCY_HZ}Hz, DUTY: {duty_freq}Hz)")

    def transmit(self, duration_s):
        """
        Generates POLARITY at FREQUENCY_HZ
        and DUTY signal based on DUTY_MODE configuration.
        FULL mode: DUTY pin stays HIGH
        HALF mode: DUTY toggles at 2× POLARITY frequency
        """
        lgpio.gpio_write(self.chip, PINS["ON_OFF"], 1)

        polarity_period = 1.0 / FREQUENCY_HZ
        
        if DUTY_MODE == "FULL":
            # FULL mode: Set DUTY HIGH and leave it there
            lgpio.gpio_write(self.chip, PINS["DUTY"], 1)
            
            end_time = time.time() + duration_s
            next_polarity_toggle = time.time()
            polarity_state = 0

            while time.time() < end_time:
                now = time.time()

                # Only toggle POLARITY in FULL mode
                if now >= next_polarity_toggle:
                    polarity_state ^= 1
                    lgpio.gpio_write(self.chip, PINS["POLARITY"], polarity_state)
                    next_polarity_toggle += polarity_period / 2.0
                    
        elif DUTY_MODE == "HALF":
            # HALF mode: Toggle DUTY at 2× POLARITY frequency
            duty_period = polarity_period / 2.0  # 2× frequency
            
            end_time = time.time() + duration_s
            next_polarity_toggle = time.time()
            next_duty_toggle = time.time()

            polarity_state = 0
            duty_state = 0

            while time.time() < end_time:
                now = time.time()

                # POLARITY toggle
                if now >= next_polarity_toggle:
                    polarity_state ^= 1
                    lgpio.gpio_write(self.chip, PINS["POLARITY"], polarity_state)
                    next_polarity_toggle += polarity_period / 2.0

                # DUTY toggle
                if now >= next_duty_toggle:
                    duty_state ^= 1
                    lgpio.gpio_write(self.chip, PINS["DUTY"], duty_state)
                    next_duty_toggle += duty_period / 2.0
        else:
            # Fallback to FULL if invalid mode specified
            timestamp = datetime.now().strftime("%H:%M:%S")
            print(f"[{timestamp}] [SIGNAL] Warning: Invalid DUTY_MODE '{DUTY_MODE}', using FULL")
            lgpio.gpio_write(self.chip, PINS["DUTY"], 1)
            
            end_time = time.time() + duration_s
            next_polarity_toggle = time.time()
            polarity_state = 0

            while time.time() < end_time:
                now = time.time()

                if now >= next_polarity_toggle:
                    polarity_state ^= 1
                    lgpio.gpio_write(self.chip, PINS["POLARITY"], polarity_state)
                    next_polarity_toggle += polarity_period / 2.0

        # End of transmission — all low
        lgpio.gpio_write(self.chip, PINS["ON_OFF"], 0)
        lgpio.gpio_write(self.chip, PINS["POLARITY"], 0)
        lgpio.gpio_write(self.chip, PINS["DUTY"], 0)

    def cleanup(self):
        """Turn off all signals when program exits"""
        try:
            timestamp = datetime.now().strftime("%H:%M:%S")
            print(f"[{timestamp}] [SIGNAL] Turning off all signals...")
            lgpio.gpio_write(self.chip, PINS["ON_OFF"], 0)
            lgpio.gpio_write(self.chip, PINS["POLARITY"], 0)
            lgpio.gpio_write(self.chip, PINS["DUTY"], 0)
            lgpio.gpiochip_close(self.chip)
        except Exception as e:
            timestamp = datetime.now().strftime("%H:%M:%S")
            print(f"[{timestamp}] [SIGNAL] Error during cleanup: {e}")

# Emergency cleanup function for when normal cleanup fails
def emergency_cleanup():
    """Force all pins LOW - call this if signals are stuck high"""
    try:
        chip = lgpio.gpiochip_open(0)
        lgpio.gpio_claim_output(chip, PINS["ON_OFF"])
        lgpio.gpio_claim_output(chip, PINS["DUTY"])
        lgpio.gpio_claim_output(chip, PINS["POLARITY"])
        lgpio.gpio_write(chip, PINS["ON_OFF"], 0)
        lgpio.gpio_write(chip, PINS["POLARITY"], 0)
        lgpio.gpio_write(chip, PINS["DUTY"], 0)
        lgpio.gpiochip_close(chip)
        timestamp = datetime.now().strftime("%H:%M:%S")
        print(f"[{timestamp}] [EMERGENCY] All signals forced LOW")
    except Exception as e:
        timestamp = datetime.now().strftime("%H:%M:%S")
        print(f"[{timestamp}] [EMERGENCY] Cleanup failed: {e}")