//! Button processing for CMDR Joystick
//!
//! Responsibilities
//! - Integrate button matrix results and extra pins
//! - Track pressed/previous states and USB change flags
//! - Detect short/long presses with minimal hold timing
//! - Filter HAT switches so only one direction remains active
//! - Evaluate special combinations (bootloader, calibration, etc.)
//! - Expose a compact state consumed by USB report generation

use crate::button_matrix::{ButtonMatrix, MatrixPins};
use crate::hardware::{AXIS_CENTER, BUTTON_COLS, BUTTON_ROWS, NUMBER_OF_BUTTONS};
use crate::mapping::*;
use embedded_hal::digital::InputPin;
use rp2040_hal::timer::Timer;

// Total buttons including the two extra (non‑matrix) buttons
pub const TOTAL_BUTTONS: usize = NUMBER_OF_BUTTONS + 2;

pub type JoystickButtonMatrix = ButtonMatrix<
    MatrixPins<{ BUTTON_ROWS }, { BUTTON_COLS }>,
    { BUTTON_ROWS },
    { BUTTON_COLS },
    { NUMBER_OF_BUTTONS },
>;

// ==================== BUTTON STRUCT ====================

#[derive(Copy, Clone, Default)]
pub struct Button {
    pub pressed: bool,
    pub previous_pressed: bool,
    pub usb_changed: bool,
    pub usb_button: usize,       // For short press
    pub usb_button_long: usize,  // For long press
    pub enable_long_press: bool, // Flag to enable special behavior
    pub enable_long_hold: bool,  // Flag to enable special behavior

    // Internals
    pub press_start_time: u32,     // When physical press started
    pub long_press_handled: bool,  // True if long press activated
    pub active_usb_button: usize,  // Currently active USB button
    pub usb_press_active: bool,    // Is USB press currently "down"
    pub usb_press_start_time: u32, // When USB press was sent
}

// ==================== SPECIAL ACTIONS ====================
/// High‑level actions triggered by dedicated button combinations.
#[derive(Debug, PartialEq)]
pub enum SpecialAction {
    None,
    Bootloader,
    StartCalibration,
    CancelCalibration,
    ThrottleHold(u16), // Value to hold
    VirtualThrottleToggle,
    CalibrationSetModeM10,
    CalibrationSetModeM7,
    CalibrationSave,
}

// ==================== BUTTON MANAGER ====================
/// Aggregates and processes all buttons, exposing a stable API to the rest of the firmware.
pub struct ButtonManager {
    pub buttons: [Button; TOTAL_BUTTONS],
}

impl Default for ButtonManager {
    fn default() -> Self {
        // Build a button manager with default-initialized buttons and state flags.
        Self::new()
    }
}

impl ButtonManager {
    pub fn new() -> Self {
        let mut buttons = [Button::default(); TOTAL_BUTTONS];

        // Configure button mappings using existing mapping functionality
        configure_button_mappings(&mut buttons);

        Self { buttons }
    }

    /// Update button states from the button matrix snapshot.
    pub fn update_from_matrix(&mut self, matrix: &mut JoystickButtonMatrix) {
        for (index, key) in matrix.buttons_pressed().iter().enumerate() {
            self.buttons[index].pressed = *key;
        }
    }

    /// Update extra (non‑matrix) button states from hardware pins.
    pub fn update_extra_buttons<L, R>(&mut self, left_pin: &mut L, right_pin: &mut R)
    where
        L: InputPin,
        R: InputPin,
        L::Error: core::fmt::Debug,
        R::Error: core::fmt::Debug,
    {
        self.buttons[BUTTON_FRONT_LEFT_EXTRA].pressed = left_pin.is_low().unwrap_or(false);
        self.buttons[BUTTON_FRONT_RIGHT_EXTRA].pressed = right_pin.is_low().unwrap_or(false);
    }

    /// Filter HAT switches so only a single direction (or center) can be active.
    pub fn filter_hat_switches(&mut self) {
        const LEFT_HAT_DIRECTIONS: [usize; 4] = [
            BUTTON_TOP_LEFT_HAT_UP,
            BUTTON_TOP_LEFT_HAT_RIGHT,
            BUTTON_TOP_LEFT_HAT_DOWN,
            BUTTON_TOP_LEFT_HAT_LEFT,
        ];

        const RIGHT_HAT_DIRECTIONS: [usize; 4] = [
            BUTTON_TOP_RIGHT_HAT_UP,
            BUTTON_TOP_RIGHT_HAT_RIGHT,
            BUTTON_TOP_RIGHT_HAT_DOWN,
            BUTTON_TOP_RIGHT_HAT_LEFT,
        ];

        self.reconcile_hat(&LEFT_HAT_DIRECTIONS, BUTTON_TOP_LEFT_HAT);
        self.reconcile_hat(&RIGHT_HAT_DIRECTIONS, BUTTON_TOP_RIGHT_HAT);
    }

    fn reconcile_hat(&mut self, directions: &[usize; 4], center: usize) {
        // Normalize hat inputs by clearing the center and conflicting directions.
        let pressed_count = directions
            .iter()
            .filter(|&&index| self.buttons[index].pressed)
            .count();

        if pressed_count == 0 {
            return;
        }

        self.buttons[center].pressed = false;

        if pressed_count >= 2 {
            for &index in directions.iter() {
                self.buttons[index].pressed = false;
            }
        }
    }

    /// Update press types (short/long) and USB change flags; returns whether USB should be updated.
    pub fn process_button_logic(&mut self, current_time: u32) -> bool {
        let mut usb_activity = false;

        for button in self.buttons.iter_mut() {
            update_button_press_type(button, current_time);

            if button.pressed != button.previous_pressed {
                button.usb_changed = true;
            }

            if button.usb_changed {
                usb_activity = true;
            }

            button.previous_pressed = button.pressed;
        }

        usb_activity
    }

    /// Convenience wrapper that derives `current_time` from the hardware timer.
    pub fn process_button_logic_with_timer(&mut self, timer: &Timer) -> bool {
        let current_time = (timer.get_counter().ticks() / 1000) as u32;
        self.process_button_logic(current_time)
    }

    /// Check for special button combinations (bootloader, calibration state/mode, VT, etc.).
    pub fn check_special_combinations(
        &self,
        unprocessed_axis_value: u16,
        calibration_active: bool,
    ) -> SpecialAction {
        // Secondary way to enter bootloader
        if self.buttons[BUTTON_FRONT_LEFT_LOWER].pressed
            && self.buttons[BUTTON_TOP_LEFT_MODE].pressed
            && self.buttons[BUTTON_TOP_RIGHT_MODE].pressed
        {
            return SpecialAction::Bootloader;
        }

        // Calibration mode toggle (start/cancel with same button combination)
        if self.buttons[BUTTON_FRONT_LEFT_UPPER].pressed
            && self.buttons[BUTTON_TOP_LEFT_MODE].pressed
            && self.buttons[BUTTON_TOP_RIGHT_MODE].pressed
        {
            if calibration_active {
                return SpecialAction::CancelCalibration;
            } else {
                return SpecialAction::StartCalibration;
            }
        }

        // Check for throttle hold button press
        if let Some(th_button) = self.get_button_press_event(TH_BUTTON) {
            if th_button {
                if unprocessed_axis_value != AXIS_CENTER {
                    return SpecialAction::ThrottleHold(unprocessed_axis_value);
                } else {
                    return SpecialAction::ThrottleHold(AXIS_CENTER);
                }
            }
        }

        // Check for virtual throttle button press
        if let Some(vt_button) = self.get_button_press_event(VT_BUTTON) {
            if vt_button {
                return SpecialAction::VirtualThrottleToggle;
            }
        }

        // Calibration mode selection (only during calibration)
        if calibration_active {
            if let Some(up_button) = self.get_button_press_event(BUTTON_TOP_LEFT_UP) {
                if up_button {
                    return SpecialAction::CalibrationSetModeM10;
                }
            }

            if let Some(down_button) = self.get_button_press_event(BUTTON_TOP_LEFT_DOWN) {
                if down_button {
                    return SpecialAction::CalibrationSetModeM7;
                }
            }

            if let Some(save_button) = self.get_button_press_event(BUTTON_TOP_RIGHT_HAT) {
                if save_button {
                    return SpecialAction::CalibrationSave;
                }
            }
        }

        SpecialAction::None
    }

    /// Get a change event for a button: Some(true)=press, Some(false)=release, None=no change.
    fn get_button_press_event(&self, button_index: usize) -> Option<bool> {
        // Report the updated pressed state whenever it differs from the previous sample.
        let button = &self.buttons[button_index];
        if button.pressed != button.previous_pressed {
            Some(button.pressed)
        } else {
            None
        }
    }

    /// Get mutable access to the internal buttons array.
    pub fn buttons_mut(&mut self) -> &mut [Button; TOTAL_BUTTONS] {
        &mut self.buttons
    }

    /// Get read‑only access to the internal buttons array.
    pub fn buttons(&self) -> &[Button; TOTAL_BUTTONS] {
        &self.buttons
    }
}

// ==================== BUTTON PRESS TYPE DETECTION ====================

/// Update button press type and manage USB press lifecycle.
///
/// Behavior
/// - On physical press: record start time and defer decision
/// - Long press: if enabled and held >= threshold, activate `usb_button_long`
/// - Short press: on release (and if long not activated), activate `usb_button`
/// - USB press lifetime: auto‑release after a minimal hold so the host sees a pulse
fn update_button_press_type(button: &mut Button, current_time: u32) {
    // Transition a single button between short/long press USB outputs.
    const LONG_PRESS_THRESHOLD: u32 = 200;

    // Pressing button
    if button.pressed && !button.previous_pressed {
        button.press_start_time = current_time;
        button.long_press_handled = false;
    }

    // While held: trigger long press if applicable
    if button.pressed
        && button.enable_long_press
        && !button.long_press_handled
        && current_time - button.press_start_time >= LONG_PRESS_THRESHOLD
    {
        button.active_usb_button = button.usb_button_long;
        button.usb_press_start_time = current_time;
        button.usb_press_active = true;
        button.usb_changed = true;
        button.long_press_handled = true;
    }

    // Releasing button
    if !button.pressed && button.previous_pressed {
        // If long press wasn't triggered, it's a short press
        if (!button.enable_long_press || !button.long_press_handled) && button.usb_button != 0 {
            button.active_usb_button = button.usb_button;
            button.usb_press_start_time = current_time;
            button.usb_press_active = true;
            button.usb_changed = true;
        }

        // If long press was active, release now
        if button.long_press_handled && button.usb_press_active {
            button.usb_changed = true;
            button.usb_press_active = false;
            button.active_usb_button = 0;
        }
    }

    // Auto‑release generated USB press after minimum hold time
    const USB_MIN_HOLD_MS: u32 = 50;
    let elapsed = current_time.saturating_sub(button.usb_press_start_time);
    let should_release = if button.long_press_handled {
        !button.enable_long_hold && elapsed >= USB_MIN_HOLD_MS
    } else {
        !button.pressed && elapsed >= USB_MIN_HOLD_MS
    };

    if button.usb_press_active && should_release {
        button.usb_changed = true;
        button.usb_press_active = false;
        button.active_usb_button = 0;
    }
}

// ==================== CONSTANTS ====================

// Special button functions (from main.rs)
pub const TH_BUTTON: usize = BUTTON_TOP_LEFT_MODE;
pub const VT_BUTTON: usize = BUTTON_TOP_RIGHT_MODE;

// ==================== TESTS ====================

#[cfg(all(test, feature = "std"))]
mod tests {
    use super::*;

    #[test]
    fn test_button_manager_creation() {
        // Button manager should allocate an entry for every physical button.
        let manager = ButtonManager::new();
        assert_eq!(manager.buttons.len(), TOTAL_BUTTONS);
    }

    #[test]
    fn test_button_default_state() {
        // Default button instances start unpressed with no lingering USB state.
        let button = Button::default();
        assert!(!button.pressed);
        assert!(!button.previous_pressed);
        assert!(!button.usb_changed);
        assert!(!button.long_press_handled);
    }

    #[test]
    fn test_special_action_combinations() {
        // The bootloader combo should trigger when all required buttons are pressed.
        let mut manager = ButtonManager::new();

        // Test bootloader combination
        manager.buttons[BUTTON_FRONT_LEFT_LOWER].pressed = true;
        manager.buttons[BUTTON_TOP_LEFT_MODE].pressed = true;
        manager.buttons[BUTTON_TOP_RIGHT_MODE].pressed = true;

        let action = manager.check_special_combinations(AXIS_CENTER, false);
        assert_eq!(action, SpecialAction::Bootloader);
    }

    #[test]
    fn test_calibration_combination() {
        // Calibration combo should generate the start-calibration action.
        let mut manager = ButtonManager::new();

        // Test calibration combination
        manager.buttons[BUTTON_FRONT_LEFT_UPPER].pressed = true;
        manager.buttons[BUTTON_TOP_LEFT_MODE].pressed = true;
        manager.buttons[BUTTON_TOP_RIGHT_MODE].pressed = true;

        let action = manager.check_special_combinations(AXIS_CENTER, false);
        assert_eq!(action, SpecialAction::StartCalibration);
    }

    #[test]
    fn test_throttle_hold_center() {
        // Throttle hold combo should capture the centered axis value when centered.
        let mut manager = ButtonManager::new();
        manager.buttons[TH_BUTTON].pressed = true;
        manager.buttons[TH_BUTTON].previous_pressed = false;

        let action = manager.check_special_combinations(AXIS_CENTER, false);
        assert_eq!(action, SpecialAction::ThrottleHold(AXIS_CENTER));
    }

    #[test]
    fn test_throttle_hold_value() {
        // Off-center throttle hold should capture the live axis value for hold.
        let mut manager = ButtonManager::new();
        manager.buttons[TH_BUTTON].pressed = true;
        manager.buttons[TH_BUTTON].previous_pressed = false;

        let test_value = 3000u16;
        let action = manager.check_special_combinations(test_value, false);
        assert_eq!(action, SpecialAction::ThrottleHold(test_value));
    }

    #[test]
    fn test_virtual_throttle_toggle() {
        // Virtual throttle button should emit the toggle action when pressed.
        let mut manager = ButtonManager::new();
        manager.buttons[VT_BUTTON].pressed = true;
        manager.buttons[VT_BUTTON].previous_pressed = false;

        let action = manager.check_special_combinations(AXIS_CENTER, false);
        assert_eq!(action, SpecialAction::VirtualThrottleToggle);
    }

    #[test]
    fn test_hat_switch_filtering_left() {
        // Left hat should clear center and conflicting directions when multiple inputs are active.
        let mut manager = ButtonManager::new();

        // Press multiple directional buttons on left hat
        manager.buttons[BUTTON_TOP_LEFT_HAT_UP].pressed = true;
        manager.buttons[BUTTON_TOP_LEFT_HAT_RIGHT].pressed = true;

        manager.filter_hat_switches();

        // Multiple directions cancel the hat output completely
        let pressed_count = (BUTTON_TOP_LEFT_HAT_UP..=BUTTON_TOP_LEFT_HAT_LEFT)
            .filter(|&i| manager.buttons[i].pressed)
            .count();
        assert_eq!(pressed_count, 0);
        assert!(!manager.buttons[BUTTON_TOP_LEFT_HAT].pressed);
    }

    #[test]
    fn test_hat_switch_filtering_right() {
        // Right hat should behave the same way, disabling conflicts and the center button.
        let mut manager = ButtonManager::new();

        // Press multiple directional buttons on right hat
        manager.buttons[BUTTON_TOP_RIGHT_HAT_UP].pressed = true;
        manager.buttons[BUTTON_TOP_RIGHT_HAT_DOWN].pressed = true;

        manager.filter_hat_switches();

        // Multiple directions cancel the hat output completely
        let pressed_count = (BUTTON_TOP_RIGHT_HAT_UP..=BUTTON_TOP_RIGHT_HAT_LEFT)
            .filter(|&i| manager.buttons[i].pressed)
            .count();
        assert_eq!(pressed_count, 0);
        assert!(!manager.buttons[BUTTON_TOP_RIGHT_HAT].pressed);
    }

    #[test]
    fn test_hat_center_button_filtering() {
        // Pressing a direction should suppress the corresponding hat center button.
        let mut manager = ButtonManager::new();

        // Press directional button and center button
        manager.buttons[BUTTON_TOP_LEFT_HAT].pressed = true;
        manager.buttons[BUTTON_TOP_LEFT_HAT_UP].pressed = true;

        manager.filter_hat_switches();

        // Center button should be disabled when directional is pressed
        assert!(!manager.buttons[BUTTON_TOP_LEFT_HAT].pressed);
        // But single direction should remain active
        assert!(manager.buttons[BUTTON_TOP_LEFT_HAT_UP].pressed);
    }

    #[test]
    fn test_hat_switch_single_direction_allowed() {
        // A single direction press must remain active for both hats.
        let mut manager = ButtonManager::new();

        // Press only one directional button on left hat
        manager.buttons[BUTTON_TOP_LEFT_HAT_UP].pressed = true;

        manager.filter_hat_switches();

        // Single direction should remain active
        assert!(manager.buttons[BUTTON_TOP_LEFT_HAT_UP].pressed);

        // Test same for right hat
        let mut manager = ButtonManager::new();
        manager.buttons[BUTTON_TOP_RIGHT_HAT_DOWN].pressed = true;

        manager.filter_hat_switches();

        // Single direction should remain active
        assert!(manager.buttons[BUTTON_TOP_RIGHT_HAT_DOWN].pressed);
    }

    #[test]
    fn test_hat_center_button_works_alone() {
        // When no direction is pressed, the center button should report as pressed.
        let mut manager = ButtonManager::new();

        // Press only center button (no directions)
        manager.buttons[BUTTON_TOP_LEFT_HAT].pressed = true;

        manager.filter_hat_switches();

        // Center button should remain active when no directions are pressed
        assert!(manager.buttons[BUTTON_TOP_LEFT_HAT].pressed);
    }

    #[test]
    fn test_button_press_type_short_press() {
        // Short presses should emit the primary USB button and flag a USB change.
        let mut button = Button::default();
        button.usb_button = 1;
        button.enable_long_press = false;

        // Press button
        button.pressed = true;
        update_button_press_type(&mut button, 100);
        button.previous_pressed = button.pressed; // Update state

        // Release button quickly (before long press threshold)
        button.pressed = false;
        update_button_press_type(&mut button, 150);

        assert_eq!(button.active_usb_button, 1);
        assert!(button.usb_press_active);
        assert!(button.usb_changed);
    }

    #[test]
    fn test_button_press_type_long_press() {
        // Long presses should switch to the alternate USB button and mark handled state.
        let mut button = Button::default();
        button.usb_button = 1;
        button.usb_button_long = 2;
        button.enable_long_press = true;

        // Press button
        button.pressed = true;
        update_button_press_type(&mut button, 100);
        button.previous_pressed = button.pressed; // Update state

        // Hold for long press threshold
        update_button_press_type(&mut button, 350); // 250ms later

        assert_eq!(button.active_usb_button, 2); // Long press button
        assert!(button.usb_press_active);
        assert!(button.long_press_handled);
    }

    #[test]
    fn test_button_press_type_long_press_auto_release_once() {
        // Non-hold long presses should auto-release once after triggering the long press.
        let mut button = Button::default();
        button.usb_button_long = 2;
        button.enable_long_press = true;
        button.enable_long_hold = false;

        // Press the button
        button.pressed = true;
        update_button_press_type(&mut button, 0);
        button.previous_pressed = button.pressed;

        // Hold long enough to trigger the long press path
        update_button_press_type(&mut button, 250);
        assert!(button.usb_press_active);
        assert_eq!(button.active_usb_button, 2);

        // Clear the changed flag to emulate USB stack observing it
        button.usb_changed = false;

        // Keep holding and ensure we auto-release exactly once
        update_button_press_type(&mut button, 320);
        assert!(!button.usb_press_active);
        assert!(button.usb_changed);

        button.usb_changed = false;
        update_button_press_type(&mut button, 400);
        assert!(!button.usb_press_active);
        assert!(!button.usb_changed);
    }

    #[test]
    fn test_timer_integration_method_exists() {
        // Document that the timer-backed helper stays callable without hardware wiring.
        let manager = ButtonManager::new();

        // This test verifies the timer integration method signature and basic functionality
        // without requiring actual hardware timer setup in the test environment.
        // The method should delegate to process_button_logic with proper time calculation.

        // Verify the ButtonManager exists and has the expected structure
        assert_eq!(manager.buttons.len(), TOTAL_BUTTONS);

        // The process_button_logic_with_timer method requires actual Timer hardware
        // which isn't available in the test environment, but the method compilation
        // is verified through the cargo check above.
    }
}
