//! Cursor grab/release handling for first-person controls.

use bevy::input::mouse::AccumulatedMouseMotion;
use bevy::prelude::*;
use bevy::window::{CursorGrabMode, CursorOptions, PrimaryWindow};
use protocol::PlayerInputIntent;
use sim::{GameInputEnabled, GameInputFocused, Player};

/// Locks and hides the cursor at startup so mouselook works immediately.
#[unsafe(no_mangle)]
pub fn grab_cursor(mut cursor: Single<&mut CursorOptions, With<PrimaryWindow>>) {
    set_grab(&mut cursor, true);
}

/// Toggles the cursor grab with `Esc` so the player can free the mouse.
#[unsafe(no_mangle)]
pub fn toggle_cursor_grab(
    keys: Res<ButtonInput<KeyCode>>,
    mut cursor: Single<&mut CursorOptions, With<PrimaryWindow>>,
) {
    if keys.just_pressed(KeyCode::Escape) {
        let grabbed = cursor.grab_mode != CursorGrabMode::None;
        set_grab(&mut cursor, !grabbed);
    }
}

/// Converts local device state into protocol-level player intent.
#[unsafe(no_mangle)]
pub fn update_local_player_intent(
    keys: Res<ButtonInput<KeyCode>>,
    mouse_motion: Res<AccumulatedMouseMotion>,
    cursor: Single<&CursorOptions, With<PrimaryWindow>>,
    input_enabled: Res<GameInputEnabled>,
    input_focused: Res<GameInputFocused>,
    mut intent: Single<&mut PlayerInputIntent, With<Player>>,
) {
    if !input_enabled.0 || !input_focused.0 {
        intent.movement = Vec2::ZERO;
        intent.sprint_held = false;
        intent.crouch_held = false;
        intent.jump_pressed = false;
        intent.look_delta = Vec2::ZERO;
        return;
    }

    let mut movement = Vec2::ZERO;
    if keys.pressed(KeyCode::KeyW) {
        movement.y += 1.0;
    }
    if keys.pressed(KeyCode::KeyS) {
        movement.y -= 1.0;
    }
    if keys.pressed(KeyCode::KeyD) {
        movement.x += 1.0;
    }
    if keys.pressed(KeyCode::KeyA) {
        movement.x -= 1.0;
    }

    intent.movement = movement.clamp_length_max(1.0);
    intent.sprint_held = keys.pressed(KeyCode::ShiftLeft);
    intent.crouch_held = keys.pressed(KeyCode::ControlLeft);
    intent.jump_pressed |= keys.just_pressed(KeyCode::Space);

    if cursor.grab_mode != CursorGrabMode::None {
        intent.look_delta += mouse_motion.delta;
    }
}

fn set_grab(cursor: &mut CursorOptions, grab: bool) {
    if grab {
        cursor.grab_mode = CursorGrabMode::Locked;
        cursor.visible = false;
    } else {
        cursor.grab_mode = CursorGrabMode::None;
        cursor.visible = true;
    }
}
