use super::{ControlState, ControllerSet};
use crate::{GameState, control::controller_common::MovementSpeedFactor, player::PlayerBodyMesh};
use bevy::prelude::*;
use happy_feet::prelude::MoveInput;
use lightyear::prelude::input::native::ActionState;
use std::f32::consts::PI;

pub struct CharacterControllerPlugin;

impl Plugin for CharacterControllerPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(
            FixedUpdate,
            (rotate_rig, apply_controls)
                .chain()
                .in_set(ControllerSet::ApplyControlsFly)
                .run_if(in_state(GameState::Playing)),
        );
    }
}

fn rotate_rig(
    actions: Query<&ActionState<ControlState>>,
    mut player: Query<(&mut Transform, &ChildOf), With<PlayerBodyMesh>>,
) {
    for (mut rig_transform, child_of) in player.iter_mut() {
        let controls = actions.get(child_of.parent()).unwrap();

        if controls.view_mode {
            continue;
        }

        let look_dir = controls.look_dir;

        // todo: Make consistent with the running controller
        let sensitivity = 0.001;
        let max_pitch = 35.0 * PI / 180.0;
        let min_pitch = -25.0 * PI / 180.0;

        rig_transform.rotate_y(look_dir.x * -sensitivity);

        let euler_rot = rig_transform.rotation.to_euler(EulerRot::YXZ);
        let yaw = euler_rot.0;
        let pitch = euler_rot.1 + look_dir.y * sensitivity;

        let pitch_clamped = pitch.clamp(min_pitch, max_pitch);
        rig_transform.rotation = Quat::from_euler(EulerRot::YXZ, yaw, pitch_clamped, 0.0);

        // The following can be used to limit the amount of rotation per frame
        // let target_rotation = rig_transform.rotation
        //     * Quat::from_rotation_x(controls.keyboard_state.look_dir.y * sensitivity);
        // let clamped_rotation = rig_transform.rotation.rotate_towards(target_rotation, 0.01);
        // rig_transform.rotation = clamped_rotation;
    }
}

fn apply_controls(
    character: Single<(&mut MoveInput, &MovementSpeedFactor)>,
    rig_transform_q: Option<Single<&GlobalTransform, With<PlayerBodyMesh>>>,
) {
    let (mut char_input, factor) = character.into_inner();

    if let Some(ref rig_transform) = rig_transform_q {
        char_input.set(-*rig_transform.forward() * factor.0);
    }
}
