use bevy::{
    input::gamepad::{GamepadAxisChangedEvent, GamepadButtonChangedEvent, GamepadEvent},
    prelude::*,
    utils::HashSet,
};

use crate::camera::DebugCamOffset;

#[derive(Default, Debug, Resource)]
pub(crate) struct InputState {
    pub yaw: f32,
    pub throttle: f32,
    pub brake: bool,
    pub pitch: f32,
}

fn update_debug_cam(mut offset: ResMut<DebugCamOffset>, mut keys: ResMut<ButtonInput<KeyCode>>) {
    let keyset: HashSet<_> = keys.get_pressed().collect();
    let shifted = keyset.contains(&KeyCode::ShiftLeft) || keyset.contains(&KeyCode::ShiftRight);

    for key in keyset {
        match key {
            KeyCode::ArrowLeft => offset.rot -= 5.0,
            KeyCode::ArrowRight => offset.rot += 5.0,
            KeyCode::ArrowUp => {
                if shifted {
                    offset.alt += 0.5;
                } else {
                    offset.dist -= 0.5;
                }
            }
            KeyCode::ArrowDown => {
                if shifted {
                    offset.alt -= 0.5;
                } else {
                    offset.dist += 0.5;
                }
            }
            _ => continue,
        }
    }

    if keys.get_just_released().len() > 0 {
        let unpressed =
            keys.just_released(KeyCode::ShiftLeft) || keys.just_released(KeyCode::ShiftRight);
        keys.reset_all();
        if shifted && !unpressed {
            keys.press(KeyCode::ShiftLeft);
        }
    }
}

fn update_input(mut events: EventReader<GamepadEvent>, mut istate: ResMut<InputState>) {
    for pad_event in events.read() {
        match pad_event {
            GamepadEvent::Button(button_event) => {
                let GamepadButtonChangedEvent {
                    button_type, value, ..
                } = button_event;
                match button_type {
                    GamepadButtonType::RightTrigger2 => istate.throttle = *value,
                    GamepadButtonType::LeftTrigger2 => istate.throttle = -value,
                    GamepadButtonType::East => {
                        if value > &0.5 {
                            istate.brake = true;
                        } else {
                            istate.brake = false;
                        }
                    }
                    _ => info!("unhandled button press: {button_event:?}"),
                }
            }
            GamepadEvent::Axis(axis_event) => {
                let GamepadAxisChangedEvent {
                    axis_type, value, ..
                } = axis_event;
                match axis_type {
                    GamepadAxisType::LeftStickX => {
                        istate.yaw = *value;
                    }
                    GamepadAxisType::RightStickY => {
                        istate.pitch = *value;
                    }
                    _ => info!("unhandled axis event: {axis_event:?}"),
                }
            }
            GamepadEvent::Connection(_) => {}
        }
    }
}

pub struct CyberInputPlugin;
impl Plugin for CyberInputPlugin {
    fn build(&self, app: &mut App) {
        app.init_resource::<InputState>()
            .add_systems(Update, (update_input, update_debug_cam));
    }
}
