//! Runtime state for the CMDR Joystick.
//!
//! This mirrors the `KeyboardState` abstraction from the keyboard refactor and
//! concentrates axis/button/calibration logic alongside USB bookkeeping.

use crate::axis::AxisManager;
use crate::buttons::{ButtonManager, SpecialAction};
use crate::calibration::CalibrationManager;
use crate::expo::ExpoLUT;
use crate::hardware;
use crate::status::SystemState;
use crate::usb_joystick_device::JoystickReport;
use crate::usb_report::get_joystick_report;
use core::fmt::Debug;
use dyn_smooth::DynamicSmootherEcoI32;
use embedded_hal::digital::InputPin;
use rp2040_hal::timer::Timer;
use usb_device::device::UsbDeviceState;

pub struct UsbState {
    pub initialized: bool,
    pub active: bool,
    pub suspended: bool,
    pub send_pending: bool,
    pub wake_on_input: bool,
    pub idle_mode: bool,
    pub activity: bool,
    activity_elapsed_ms: u32,
}

impl UsbState {
    pub const fn new() -> Self {
        Self {
            initialized: false,
            active: false,
            suspended: false,
            send_pending: false,
            wake_on_input: false,
            idle_mode: false,
            activity: false,
            activity_elapsed_ms: 0,
        }
    }

    pub fn on_poll(&mut self) {
        // Called on every USB poll; mark device active and wake from idle.
        if !self.initialized {
            self.initialized = true;
        }
        if !self.active {
            self.mark_activity();
        }
        self.active = true;
    }

    pub fn mark_activity(&mut self) {
        // Flag that input activity occurred and reset idle counters.
        self.activity = true;
        self.activity_elapsed_ms = 0;
        self.idle_mode = false;
        self.send_pending = true;
    }

    pub fn handle_input_activity(&mut self) {
        // Treat input changes as activity and request wake from suspend.
        self.mark_activity();
        if self.suspended && self.wake_on_input {
            self.wake_on_input = false;
        }
    }

    pub fn on_suspend_change(&mut self, state: UsbDeviceState) {
        // Update suspend bookkeeping when USB state changes.
        let was_suspended = self.suspended;
        self.suspended = state == UsbDeviceState::Suspend;

        match (was_suspended, self.suspended) {
            (true, false) => {
                self.mark_activity();
                self.wake_on_input = false;
            }
            (false, true) => {
                self.idle_mode = true;
                self.activity = false;
                self.send_pending = false;
                self.wake_on_input = true;
            }
            _ => {}
        }
    }

    pub fn advance_idle_timer(&mut self, interval_ms: u32) {
        // Age the activity timer, transitioning to idle after the timeout.
        if !self.activity {
            return;
        }
        self.activity_elapsed_ms = self.activity_elapsed_ms.saturating_add(interval_ms);
        if self.activity_elapsed_ms >= hardware::timers::USB_ACTIVITY_TIMEOUT_MS {
            self.activity = false;
            self.activity_elapsed_ms = 0;
            self.idle_mode = true;
            self.send_pending = false;
        }
    }

    pub fn acknowledge_report(&mut self) {
        // Reset pending state once the host accepts a report.
        self.send_pending = false;
    }
}

pub struct JoystickState {
    axis_manager: AxisManager,
    button_manager: ButtonManager,
    calibration_manager: CalibrationManager,
    smoother: [DynamicSmootherEcoI32; hardware::NBR_OF_GIMBAL_AXIS],
    expo_primary: ExpoLUT,
    expo_virtual: ExpoLUT,
    vt_enable: bool,
    usb: UsbState,
}

impl JoystickState {
    pub fn new() -> Self {
        // Initialize managers, smoothers, expo curves, and USB state.
        Self {
            axis_manager: AxisManager::new(),
            button_manager: ButtonManager::new(),
            calibration_manager: CalibrationManager::new(),
            smoother: AxisManager::create_smoothers(),
            expo_primary: ExpoLUT::new(0.3),
            expo_virtual: ExpoLUT::new(0.6),
            vt_enable: false,
            usb: UsbState::new(),
        }
    }

    pub fn load_calibration<R>(&mut self, mut read_fn: R)
    where
        R: FnMut(u32) -> Result<u8, ()>,
    {
        // Fetch stored axis calibration and gimbal mode from persistent storage.
        CalibrationManager::load_axis_calibration(&mut self.axis_manager.axes, &mut read_fn);
        let gimbal_mode = CalibrationManager::load_gimbal_mode(&mut read_fn);
        self.axis_manager.set_gimbal_mode(gimbal_mode);
        self.calibration_manager.set_gimbal_mode(gimbal_mode);
    }

    pub fn update_button_states<L, R>(
        &mut self,
        matrix: &mut crate::board::JoystickMatrix,
        left_button: &mut L,
        right_button: &mut R,
    ) where
        L: InputPin,
        R: InputPin,
        L::Error: Debug,
        R::Error: Debug,
    {
        // Refresh matrix-driven and discrete button inputs, then normalize hats.
        self.button_manager.update_from_matrix(matrix);
        self.button_manager
            .update_extra_buttons(left_button, right_button);
        self.button_manager.filter_hat_switches();
    }

    pub fn finalize_button_logic(&mut self, timer: &Timer) -> bool {
        // Update per-button timers and USB state using the shared hardware timer.
        self.button_manager.process_button_logic_with_timer(timer)
    }

    pub fn check_special_action(&self) -> SpecialAction {
        // Inspect button state for bootloader/calibration/hold command combinations.
        self.button_manager.check_special_combinations(
            self.axis_manager.get_value_before_hold(),
            self.calibration_manager.is_active(),
        )
    }

    pub fn handle_special_action<W>(&mut self, action: SpecialAction, mut write_page: W)
    where
        W: FnMut(u32, &[u8]) -> Result<(), ()>,
    {
        // Execute the requested special action, updating calibration/throttle state as needed.
        match action {
            SpecialAction::Bootloader => {}
            SpecialAction::StartCalibration => {
                for (index, axis) in self.axis_manager.axes.iter_mut().enumerate() {
                    let centered = self.smoother[index].value() as u16;
                    axis.center = centered;
                    axis.min = centered;
                    axis.max = centered;
                }
                self.axis_manager.clear_throttle_hold();
                self.calibration_manager.start_calibration();
            }
            SpecialAction::CancelCalibration => {
                self.calibration_manager.stop_calibration();
            }
            SpecialAction::ThrottleHold(value) => {
                self.axis_manager.set_throttle_hold(value);
            }
            SpecialAction::VirtualThrottleToggle => {
                self.vt_enable = !self.vt_enable;
            }
            SpecialAction::CalibrationSetModeM10 => {
                if self
                    .calibration_manager
                    .set_gimbal_mode_m10(&mut self.axis_manager.axes, &self.smoother)
                {
                    self.axis_manager
                        .set_gimbal_mode(self.calibration_manager.get_gimbal_mode());
                    self.axis_manager.clear_throttle_hold();
                }
            }
            SpecialAction::CalibrationSetModeM7 => {
                if self
                    .calibration_manager
                    .set_gimbal_mode_m7(&mut self.axis_manager.axes, &self.smoother)
                {
                    self.axis_manager
                        .set_gimbal_mode(self.calibration_manager.get_gimbal_mode());
                    self.axis_manager.clear_throttle_hold();
                }
            }
            SpecialAction::CalibrationSave => {
                self.calibration_manager
                    .save_calibration(&self.axis_manager.axes, &mut |page, data| {
                        write_page(page, data)
                    });
            }
            SpecialAction::None => {}
        }
    }

    pub fn update_calibration_tracking(&mut self) {
        self.calibration_manager
            .update_dynamic_calibration(&mut self.axis_manager.axes, &self.smoother);
    }

    pub fn tick_smoothers(&mut self, raw: &mut [u16; hardware::NBR_OF_GIMBAL_AXIS]) {
        self.axis_manager.apply_gimbal_compensation(raw);
        self.axis_manager.update_smoothers(&mut self.smoother, raw);
    }

    pub fn process_axes(&mut self) -> bool {
        self.axis_manager
            .process_axis_values(&self.smoother, &self.expo_primary)
    }

    pub fn update_virtual_axes(&mut self) -> bool {
        self.axis_manager
            .update_virtual_axes(self.button_manager.buttons(), self.vt_enable)
    }

    pub fn vt_enable(&self) -> bool {
        self.vt_enable
    }

    pub fn usb_state(&mut self) -> &mut UsbState {
        &mut self.usb
    }

    pub fn axis_manager(&mut self) -> &mut AxisManager {
        &mut self.axis_manager
    }

    pub fn button_manager(&mut self) -> &mut ButtonManager {
        &mut self.button_manager
    }

    pub fn expo_virtual(&self) -> &ExpoLUT {
        &self.expo_virtual
    }

    pub fn smoother(&self) -> &[DynamicSmootherEcoI32; hardware::NBR_OF_GIMBAL_AXIS] {
        &self.smoother
    }

    pub fn calibration_manager(&self) -> &CalibrationManager {
        &self.calibration_manager
    }

    pub fn system_state(&self) -> SystemState {
        SystemState {
            usb_active: self.usb.active,
            usb_initialized: self.usb.initialized,
            usb_suspended: self.usb.suspended,
            idle_mode: self.usb.idle_mode,
            calibration_active: self.calibration_manager.is_active(),
            throttle_hold_enable: self.axis_manager.throttle_hold_enable,
            vt_enable: self.vt_enable,
        }
    }

    pub fn build_report(&mut self) -> JoystickReport {
        let virtual_ry = self.axis_manager.get_virtual_ry_value(&self.expo_virtual);
        let virtual_rz = self.axis_manager.get_virtual_rz_value(&self.expo_virtual);
        get_joystick_report(
            self.button_manager.buttons_mut(),
            &mut self.axis_manager.axes,
            virtual_ry,
            virtual_rz,
            &self.vt_enable,
        )
    }

    pub fn empty_report() -> JoystickReport {
        JoystickReport {
            x: 0,
            y: 0,
            z: 0,
            rx: 0,
            ry: 0,
            rz: 0,
            slider: 0,
            hat: 8,
            buttons: 0,
        }
    }
}
