use bevy::prelude::*;

use crate::{
    GameState,
    head::ActiveHead,
    heads_database::{HeadControls, HeadsDatabase},
};

mod collisions;
pub mod controller_common;
pub mod controller_flying;
pub mod controller_running;
pub mod controls;

#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone, Default)]
enum ControllerSet {
    CollectInputs,
    ApplyControlsFly,
    #[default]
    ApplyControlsRun,
}

#[derive(Resource, Debug, Clone, Copy, Default, PartialEq)]
pub struct ControlState {
    /// Movement direction with a maximum length of 1.0
    pub move_dir: Vec2,
    pub look_dir: Vec2,
    pub jump: bool,
    /// Determines if the camera can rotate freely around the player
    pub view_mode: bool,
}

#[derive(Resource, Debug, Default)]
struct Controls {
    keyboard_state: ControlState,
    gamepad_state: Option<ControlState>,
}

#[derive(Event)]
pub struct ControllerSwitchEvent;

#[derive(Resource, Debug, Default, PartialEq)]
pub struct SelectedController(ControllerSet);

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

    app.add_plugins(controls::plugin);
    app.add_plugins(controller_common::plugin);
    app.add_plugins(controller_flying::CharacterControllerPlugin);
    app.add_plugins(controller_running::CharacterControllerPlugin);

    app.add_event::<ControllerSwitchEvent>();

    app.configure_sets(
        Update,
        (
            ControllerSet::CollectInputs,
            ControllerSet::ApplyControlsFly.run_if(resource_equals(SelectedController(
                ControllerSet::ApplyControlsFly,
            ))),
            ControllerSet::ApplyControlsRun.run_if(resource_equals(SelectedController(
                ControllerSet::ApplyControlsRun,
            ))),
        )
            .chain()
            .run_if(in_state(GameState::Playing)),
    );

    app.add_systems(Update, head_change.run_if(in_state(GameState::Playing)));
}

fn head_change(
    query: Query<&ActiveHead, Changed<ActiveHead>>,
    heads_db: Res<HeadsDatabase>,
    mut selected_controller: ResMut<SelectedController>,
    mut event_controller_switch: EventWriter<ControllerSwitchEvent>,
) {
    for head in query.iter() {
        let stats = heads_db.head_stats(head.0);
        let controller = match stats.controls {
            HeadControls::Plane => ControllerSet::ApplyControlsFly,
            HeadControls::Walk => ControllerSet::ApplyControlsRun,
        };

        if selected_controller.0 != controller {
            event_controller_switch.write(ControllerSwitchEvent);

            selected_controller.0 = controller;
        }
    }
}
