import threading
import time

class DeadbandController:
    """
    Controls deadbands for duty and polarity signals.
    Allows configurable timing offsets for rising and falling edges.
    """
    
    def __init__(self):
        # Deadband timing configuration (in seconds)
        # Positive values delay the edge, negative values advance it
        self.duty_rising_deadband = +0.000   # 5ms delay on rising edge
        self.duty_falling_deadband = +0.000  # 5ms delay on falling edge
        self.polarity_rising_deadband = +0.000   # 5ms delay on rising edge
        self.polarity_falling_deadband = +0.000  # 5ms delay on falling edge
        
        # Internal state tracking
        self.duty_last_state = False
        self.polarity_last_state = False
        
        # Scheduled edge callbacks
        self.duty_timer = None
        self.polarity_timer = None
        
        # Lock for thread safety
        self.lock = threading.Lock()
    
    def configure_deadbands(self, duty_rising=None, duty_falling=None, 
                           polarity_rising=None, polarity_falling=None):
        """
        Configure deadband timing for signal edges.
        
        Args:
            duty_rising: Deadband for duty signal rising edge (seconds)
            duty_falling: Deadband for duty signal falling edge (seconds)
            polarity_rising: Deadband for polarity signal rising edge (seconds)
            polarity_falling: Deadband for polarity signal falling edge (seconds)
        """
        with self.lock:
            if duty_rising is not None:
                self.duty_rising_deadband = duty_rising
            if duty_falling is not None:
                self.duty_falling_deadband = duty_falling
            if polarity_rising is not None:
                self.polarity_rising_deadband = polarity_rising
            if polarity_falling is not None:
                self.polarity_falling_deadband = polarity_falling
    
    def set_duty_deadbands(self, rising_ms=-5, falling_ms=5):
        """
        Convenience method to set duty deadbands in milliseconds.
        
        Args:
            rising_ms: Rising edge deadband in milliseconds
            falling_ms: Falling edge deadband in milliseconds
        """
        self.configure_deadbands(
            duty_rising=rising_ms / 1000.0,
            duty_falling=falling_ms / 1000.0
        )
    
    def set_polarity_deadbands(self, rising_ms=0, falling_ms=0):
        """
        Convenience method to set polarity deadbands in milliseconds.
        
        Args:
            rising_ms: Rising edge deadband in milliseconds
            falling_ms: Falling edge deadband in milliseconds
        """
        self.configure_deadbands(
            polarity_rising=rising_ms / 1000.0,
            polarity_falling=falling_ms / 1000.0
        )
    
    def apply_duty_deadband(self, new_state, callback):
        """
        Apply deadband to duty signal state change.
        
        Args:
            new_state: New boolean state for duty signal
            callback: Function to call with the final state after deadband
        """
        with self.lock:
            # Cancel any pending timer for this signal
            if self.duty_timer is not None:
                self.duty_timer.cancel()
                self.duty_timer = None
            
            # Determine if this is a rising or falling edge
            if new_state != self.duty_last_state:
                if new_state:  # Rising edge
                    deadband_time = self.duty_rising_deadband
                else:  # Falling edge
                    deadband_time = self.duty_falling_deadband
                
                # Update last state immediately
                self.duty_last_state = new_state
                
                # If deadband is zero or negative, apply immediately
                if deadband_time <= 0:
                    callback(new_state)
                else:
                    # Schedule the callback after deadband delay
                    self.duty_timer = threading.Timer(deadband_time, callback, args=[new_state])
                    self.duty_timer.start()
            else:
                # No state change, apply immediately
                callback(new_state)
    
    def apply_polarity_deadband(self, new_state, callback):
        """
        Apply deadband to polarity signal state change.
        
        Args:
            new_state: New boolean state for polarity signal
            callback: Function to call with the final state after deadband
        """
        with self.lock:
            # Cancel any pending timer for this signal
            if self.polarity_timer is not None:
                self.polarity_timer.cancel()
                self.polarity_timer = None
            
            # Determine if this is a rising or falling edge
            if new_state != self.polarity_last_state:
                if new_state:  # Rising edge
                    deadband_time = self.polarity_rising_deadband
                else:  # Falling edge
                    deadband_time = self.polarity_falling_deadband
                
                # Update last state immediately
                self.polarity_last_state = new_state
                
                # If deadband is zero or negative, apply immediately
                if deadband_time <= 0:
                    callback(new_state)
                else:
                    # Schedule the callback after deadband delay
                    self.polarity_timer = threading.Timer(deadband_time, callback, args=[new_state])
                    self.polarity_timer.start()
            else:
                # No state change, apply immediately
                callback(new_state)
    
    def reset_state(self):
        """Reset internal state tracking and cancel any pending timers."""
        with self.lock:
            # Cancel pending timers
            if self.duty_timer is not None:
                self.duty_timer.cancel()
                self.duty_timer = None
            if self.polarity_timer is not None:
                self.polarity_timer.cancel()
                self.polarity_timer = None
            
            # Reset state tracking
            self.duty_last_state = False
            self.polarity_last_state = False
    
    def get_deadband_info(self):
        """
        Get current deadband configuration information.
        
        Returns:
            Dictionary with current deadband settings
        """
        with self.lock:
            return {
                'duty_rising_ms': self.duty_rising_deadband * 1000,
                'duty_falling_ms': self.duty_falling_deadband * 1000,
                'polarity_rising_ms': self.polarity_rising_deadband * 1000,
                'polarity_falling_ms': self.polarity_falling_deadband * 1000,
                'duty_state': self.duty_last_state,
                'polarity_state': self.polarity_last_state
            }


# Convenience functions for easy configuration
def create_deadband_controller(duty_ms=5, polarity_ms=5):
    """
    Create a deadband controller with symmetric deadbands.
    
    Args:
        duty_ms: Deadband for duty signal edges (milliseconds)
        polarity_ms: Deadband for polarity signal edges (milliseconds)
    
    Returns:
        DeadbandController instance
    """
    controller = DeadbandController()
    controller.set_duty_deadbands(duty_ms, duty_ms)
    controller.set_polarity_deadbands(polarity_ms, polarity_ms)
    return controller


def create_asymmetric_deadband_controller(duty_rising_ms=5, duty_falling_ms=5,
                                        polarity_rising_ms=5, polarity_falling_ms=5):
    """
    Create a deadband controller with asymmetric deadbands.
    
    Args:
        duty_rising_ms: Duty signal rising edge deadband (milliseconds)
        duty_falling_ms: Duty signal falling edge deadband (milliseconds)
        polarity_rising_ms: Polarity signal rising edge deadband (milliseconds)
        polarity_falling_ms: Polarity signal falling edge deadband (milliseconds)
    
    Returns:
        DeadbandController instance
    """
    controller = DeadbandController()
    controller.configure_deadbands(
        duty_rising=duty_rising_ms / 1000.0,
        duty_falling=duty_falling_ms / 1000.0,
        polarity_rising=polarity_rising_ms / 1000.0,
        polarity_falling=polarity_falling_ms / 1000.0
    )
    return controller


# Example usage and configuration section
if __name__ == "__main__":
    # Example 1: Basic symmetric deadbands
    print("Example 1: Symmetric 5ms deadbands")
    controller = create_deadband_controller(duty_ms=5, polarity_ms=5)
    print(controller.get_deadband_info())
    
    # Example 2: Asymmetric deadbands
    print("\nExample 2: Asymmetric deadbands")
    controller2 = create_asymmetric_deadband_controller(
        duty_rising_ms=3,     # 3ms delay on duty rising
        duty_falling_ms=7,    # 7ms delay on duty falling
        polarity_rising_ms=2, # 2ms delay on polarity rising
        polarity_falling_ms=8 # 8ms delay on polarity falling
    )
    print(controller2.get_deadband_info())
    
    # Example 3: Easy modification of existing controller
    print("\nExample 3: Modifying deadbands")
    controller.set_duty_deadbands(rising_ms=10, falling_ms=2)
    controller.set_polarity_deadbands(rising_ms=1, falling_ms=15)
    print(controller.get_deadband_info())