//! Project: CMtec CMDR joystick 25
//! Date:    2025-09-13
//! Author:  Christoffer Martinsson
//! Email:   cm@cmtec.se
//! License: Please refer to LICENSE in root directory

use rp2040_hal::{
    gpio::AnyPin,
    pio::{PIO, PIOExt, StateMachineIndex, UninitStateMachine},
};
use smart_leds::{RGB8, SmartLedsWrite};
use ws2812_pio::Ws2812Direct;

/// Status LED modes with clear semantic meaning
#[allow(dead_code)]
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum StatusMode {
    Off = 0,
    Normal = 1,
    NormalFlash = 2,
    Activity = 3,
    ActivityFlash = 4,
    Other = 5,
    OtherFlash = 6,
    Warning = 7,
    Error = 8,
    Bootloader = 9,
}

/// System state for LED status indication
#[derive(Clone, Copy)]
pub struct SystemState {
    pub usb_active: bool,
    pub calibration_active: bool,
    pub throttle_hold_enable: bool,
    pub vt_enable: bool,
}

/// Color definitions for different status modes
const LED_COLORS: [RGB8; 10] = [
    RGB8 { r: 0, g: 0, b: 0 },     // Off
    RGB8 { r: 10, g: 7, b: 0 },    // Normal (Green)
    RGB8 { r: 10, g: 7, b: 0 },    // NormalFlash (Green)
    RGB8 { r: 10, g: 4, b: 10 },   // Activity (Blue)
    RGB8 { r: 10, g: 4, b: 10 },   // ActivityFlash (Blue)
    RGB8 { r: 5, g: 10, b: 0 },    // Other (Orange)
    RGB8 { r: 5, g: 10, b: 0 },    // OtherFlash (Orange)
    RGB8 { r: 2, g: 20, b: 0 },    // Warning (Red)
    RGB8 { r: 2, g: 20, b: 0 },    // Error (Red)
    RGB8 { r: 0, g: 10, b: 10 },   // Bootloader (Purple)
];

/// Improved Status LED driver with self-contained state management
pub struct StatusLed<P, SM, I>
where
    I: AnyPin<Function = P::PinFunction>,
    P: PIOExt,
    SM: StateMachineIndex,
{
    ws2812_direct: Ws2812Direct<P, SM, I>,
    current_mode: StatusMode,
    flash_state: bool,
    last_update_time: Option<u32>,
}

impl<P, SM, I> StatusLed<P, SM, I>
where
    I: AnyPin<Function = P::PinFunction>,
    P: PIOExt,
    SM: StateMachineIndex,
{
    /// Creates a new StatusLed instance
    ///
    /// # Arguments
    /// * `pin` - PIO pin for WS2812 LED
    /// * `pio` - PIO instance
    /// * `sm` - PIO state machine
    /// * `clock_freq` - PIO clock frequency
    pub fn new(
        pin: I,
        pio: &mut PIO<P>,
        sm: UninitStateMachine<(P, SM)>,
        clock_freq: fugit::HertzU32,
    ) -> Self {
        let ws2812_direct = Ws2812Direct::new(pin, pio, sm, clock_freq);
        Self {
            ws2812_direct,
            current_mode: StatusMode::Off,
            flash_state: false,
            last_update_time: None,
        }
    }

    /// Update LED based on system state - main interface for main.rs
    ///
    /// This replaces the old update_status_led() function from main.rs
    /// # Arguments
    /// * `system_state` - Current system state
    /// * `current_time` - Current time in milliseconds for flash timing
    pub fn update_from_system_state(&mut self, system_state: SystemState, current_time: u32) {
        let desired_mode = if system_state.calibration_active {
            StatusMode::ActivityFlash
        } else if !system_state.usb_active {
            StatusMode::NormalFlash
        } else if system_state.usb_active && system_state.vt_enable {
            StatusMode::Activity
        } else if system_state.usb_active && system_state.throttle_hold_enable {
            StatusMode::Other
        } else if system_state.usb_active {
            StatusMode::Normal
        } else {
            StatusMode::Off
        };

        self.set_mode(desired_mode, current_time);
    }

    /// Set LED mode directly
    /// # Arguments
    /// * `mode` - Desired status mode
    /// * `current_time` - Current time in milliseconds
    pub fn set_mode(&mut self, mode: StatusMode, current_time: u32) {
        // Force update if mode changed
        let force_update = mode != self.current_mode;
        self.current_mode = mode;

        self.update_display(current_time, force_update);
    }

    /// Update LED display based on current mode and timing
    /// Called periodically to handle flashing
    /// # Arguments
    /// * `current_time` - Current time in milliseconds
    pub fn update_display(&mut self, current_time: u32, force_update: bool) {
        let should_update = force_update || self.should_flash_now(current_time);

        if !should_update {
            return;
        }

        self.last_update_time = Some(current_time);

        match self.current_mode {
            // Flashing modes - toggle between on and off
            StatusMode::NormalFlash
            | StatusMode::ActivityFlash
            | StatusMode::OtherFlash
            | StatusMode::Warning => {
                if self.flash_state {
                    // Show the color
                    self.write_color(LED_COLORS[self.current_mode as usize]);
                } else {
                    // Show off (black)
                    self.write_color(LED_COLORS[StatusMode::Off as usize]);
                }
                self.flash_state = !self.flash_state;
            },
            // Solid modes - just show the color
            _ => {
                self.write_color(LED_COLORS[self.current_mode as usize]);
                self.flash_state = true; // Reset flash state for next flash mode
            }
        }
    }

    /// Get current status mode
    #[allow(dead_code)]
    pub fn get_mode(&self) -> StatusMode {
        self.current_mode
    }

    /// Check if it's time to update flashing LED
    fn should_flash_now(&self, current_time: u32) -> bool {
        match self.last_update_time {
            None => true, // First update
            Some(last_time) => {
                // Flash every ~500ms for flashing modes
                match self.current_mode {
                    StatusMode::NormalFlash
                    | StatusMode::ActivityFlash
                    | StatusMode::OtherFlash
                    | StatusMode::Warning => {
                        current_time.saturating_sub(last_time) >= 500
                    },
                    _ => false // Non-flashing modes don't need periodic updates
                }
            }
        }
    }

    /// Write color to LED
    fn write_color(&mut self, color: RGB8) {
        let _ = self.ws2812_direct.write([color].iter().copied());
    }
}

impl<P, SM, I> StatusLed<P, SM, I>
where
    I: AnyPin<Function = P::PinFunction>,
    P: PIOExt,
    SM: StateMachineIndex,
{
    /// Legacy interface for compatibility - direct mode update
    /// This maintains compatibility with existing direct update calls
    pub fn update(&mut self, mode: StatusMode) {
        // Use a dummy time for immediate updates
        self.set_mode(mode, 0);
    }
}

