use crate::{
    GameState,
    abilities::{TriggerCashHeal, TriggerState},
    backpack::BackpackAction,
    heads::SelectActiveHead,
};
use bevy::{
    input::{
        ButtonState,
        gamepad::{GamepadConnection, GamepadEvent},
        mouse::{MouseButtonInput, MouseMotion},
    },
    prelude::*,
};

use super::{ControlState, ControllerSet, Controls};

pub fn plugin(app: &mut App) {
    app.init_resource::<Controls>();

    app.add_systems(
        Update,
        (
            gamepad_controls,
            keyboard_controls,
            mouse_rotate,
            mouse_click.run_if(on_event::<MouseButtonInput>),
            gamepad_connections.run_if(on_event::<GamepadEvent>),
            combine_controls,
        )
            .chain()
            .in_set(ControllerSet::CollectInputs)
            .run_if(in_state(GameState::Playing)),
    );
}

fn combine_controls(controls: Res<Controls>, mut combined_controls: ResMut<ControlState>) {
    let keyboard = controls.keyboard_state;

    if let Some(gamepad) = controls.gamepad_state {
        combined_controls.look_dir = gamepad.look_dir + keyboard.look_dir;
        combined_controls.move_dir = gamepad.move_dir + keyboard.move_dir;
        combined_controls.jump = gamepad.jump | keyboard.jump;
        combined_controls.view_mode = gamepad.view_mode | keyboard.view_mode;
    } else {
        combined_controls.look_dir = keyboard.look_dir;
        combined_controls.move_dir = keyboard.move_dir;
        combined_controls.jump = keyboard.jump;
        combined_controls.view_mode = keyboard.view_mode;
    };
}

/// Applies a square deadzone to a Vec2
fn deadzone_square(v: Vec2, min: f32) -> Vec2 {
    Vec2::new(
        if v.x.abs() < min { 0. } else { v.x },
        if v.y.abs() < min { 0. } else { v.y },
    )
}

fn gamepad_controls(
    mut commands: Commands,
    gamepads: Query<(Entity, &Gamepad)>,
    mut controls: ResMut<Controls>,
) {
    let Some((_e, gamepad)) = gamepads.iter().next() else {
        if controls.gamepad_state.is_some() {
            controls.gamepad_state = None;
        }
        return;
    };

    let deadzone_left_stick = 0.15;
    let deadzone_right_stick = 0.15;

    // info!("gamepad: {:?}", gamepad);

    let rotate = gamepad
        .get(GamepadButton::RightTrigger2)
        .unwrap_or_default();

    // 8BitDo Ultimate wireless Controller for PC
    let look_dir = if gamepad.vendor_id() == Some(11720) && gamepad.product_id() == Some(12306) {
        const EPSILON: f32 = 0.015;
        Vec2::new(
            if rotate < 0.5 - EPSILON {
                40. * (rotate - 0.5)
            } else if rotate > 0.5 + EPSILON {
                -40. * (rotate - 0.5)
            } else {
                0.
            },
            0.,
        )
    } else {
        deadzone_square(gamepad.right_stick(), deadzone_right_stick) * 40.
    };

    let state = ControlState {
        move_dir: deadzone_square(gamepad.left_stick(), deadzone_left_stick),
        look_dir,
        jump: gamepad.pressed(GamepadButton::South),
        view_mode: gamepad.pressed(GamepadButton::LeftTrigger2),
    };

    if gamepad.just_pressed(GamepadButton::RightTrigger2) {
        commands.trigger(TriggerState::Active);
    }
    if gamepad.just_released(GamepadButton::RightTrigger2) {
        commands.trigger(TriggerState::Inactive);
    }
    if gamepad.just_pressed(GamepadButton::LeftTrigger) {
        commands.trigger(SelectActiveHead::Left);
    }
    if gamepad.just_pressed(GamepadButton::RightTrigger) {
        commands.trigger(SelectActiveHead::Right);
    }
    if gamepad.just_pressed(GamepadButton::DPadLeft) {
        commands.trigger(BackpackAction::Left);
    }
    if gamepad.just_pressed(GamepadButton::DPadRight) {
        commands.trigger(BackpackAction::Right);
    }
    if gamepad.just_pressed(GamepadButton::DPadDown) {
        commands.trigger(BackpackAction::Swap);
    }
    if gamepad.just_pressed(GamepadButton::DPadUp) {
        commands.trigger(BackpackAction::OpenClose);
    }
    if gamepad.just_pressed(GamepadButton::East) {
        commands.trigger(TriggerCashHeal);
    }

    if controls
        .gamepad_state
        .as_ref()
        .map(|last_state| *last_state != state)
        .unwrap_or(true)
    {
        // info!("gamepad state changed: {:?}", state);
        controls.gamepad_state = Some(state);
    }
}

fn mouse_rotate(mut mouse: EventReader<MouseMotion>, mut controls: ResMut<Controls>) {
    controls.keyboard_state.look_dir = Vec2::ZERO;

    for ev in mouse.read() {
        controls.keyboard_state.look_dir += ev.delta;
    }
}

fn keyboard_controls(
    mut commands: Commands,
    keyboard: Res<ButtonInput<KeyCode>>,
    mut controls: ResMut<Controls>,
) {
    let up_binds = [KeyCode::KeyW, KeyCode::ArrowUp];
    let down_binds = [KeyCode::KeyS, KeyCode::ArrowDown];
    let left_binds = [KeyCode::KeyA, KeyCode::ArrowLeft];
    let right_binds = [KeyCode::KeyD, KeyCode::ArrowRight];

    let up = keyboard.any_pressed(up_binds);
    let down = keyboard.any_pressed(down_binds);
    let left = keyboard.any_pressed(left_binds);
    let right = keyboard.any_pressed(right_binds);

    let horizontal = right as i8 - left as i8;
    let vertical = up as i8 - down as i8;
    let direction = Vec2::new(horizontal as f32, vertical as f32).clamp_length_max(1.0);

    if keyboard.just_pressed(KeyCode::KeyB) {
        commands.trigger(BackpackAction::OpenClose);
    }
    if keyboard.just_pressed(KeyCode::Enter) {
        commands.trigger(BackpackAction::Swap);
    }
    if keyboard.just_pressed(KeyCode::Comma) {
        commands.trigger(BackpackAction::Left);
    }
    if keyboard.just_pressed(KeyCode::Period) {
        commands.trigger(BackpackAction::Right);
    }

    if keyboard.just_pressed(KeyCode::KeyQ) {
        commands.trigger(SelectActiveHead::Left);
    }
    if keyboard.just_pressed(KeyCode::KeyE) {
        commands.trigger(SelectActiveHead::Right);
    }
    if keyboard.just_pressed(KeyCode::Enter) {
        commands.trigger(TriggerCashHeal);
    }

    controls.keyboard_state.move_dir = direction;
    controls.keyboard_state.jump = keyboard.pressed(KeyCode::Space);
    controls.keyboard_state.view_mode = keyboard.pressed(KeyCode::Tab);
}

fn mouse_click(mut events: EventReader<MouseButtonInput>, mut commands: Commands) {
    for ev in events.read() {
        match ev {
            MouseButtonInput {
                button: MouseButton::Left,
                state: ButtonState::Pressed,
                ..
            } => {
                commands.trigger(TriggerState::Active);
            }
            MouseButtonInput {
                button: MouseButton::Left,
                state: ButtonState::Released,
                ..
            } => {
                commands.trigger(TriggerState::Inactive);
            }
            _ => {}
        }
    }
}

fn gamepad_connections(mut evr_gamepad: EventReader<GamepadEvent>) {
    for ev in evr_gamepad.read() {
        if let GamepadEvent::Connection(connection) = ev {
            match &connection.connection {
                GamepadConnection::Connected {
                    name,
                    vendor_id,
                    product_id,
                } => {
                    info!(
                        "New gamepad connected: {:?}, name: {name}, vendor: {vendor_id:?}, product: {product_id:?}",
                        connection.gamepad,
                    );
                }
                GamepadConnection::Disconnected => {
                    info!("Lost connection with gamepad: {:?}", connection.gamepad);
                }
            }
        }
    }
}
