//! Default editor input bindings + the action-name constants the bindings
//! UI and any debug overlay address.
//!
//! Living in the editor library (not the binary) so the Stage-7
//! [`InputBindings`](crate::shell::Shell) preferences page and any future
//! editor module can re-register or remap the same actions without
//! depending on the binary's private module.

use oxide_engine::input::{ActionMap, AxisBinding, Binding};
use oxide_engine::winit::keyboard::KeyCode;

/// The settings-section name under which the editor's
/// [`ActionOverrides`](oxide_engine::input::ActionOverrides) are persisted.
///
/// The shell registers this section automatically in
/// [`EditorState::new`](crate::state::EditorState::new); UI code that wants
/// to refresh the section after a binding change addresses it by this name.
pub const SETTINGS_SECTION: &str = "input.bindings";

/// Stable action names addressed throughout the editor — the bindings
/// preferences page, the camera input poll in the runner, and any future
/// debug overlay all reference these strings.
pub mod action {
    /// Button: toggle between orbit and flythrough viewport cameras.
    pub const TOGGLE_FLYTHROUGH: &str = "editor.camera.toggle_flythrough";
    /// Button (held): accelerate flythrough translation while engaged.
    pub const SPRINT: &str = "editor.camera.sprint";
    /// 1D axis: strafe right (+) / strafe left (−) in flythrough mode.
    pub const MOVE_RIGHT: &str = "editor.camera.move_right";
    /// 1D axis: forward (+) / back (−) in flythrough mode.
    pub const MOVE_FORWARD: &str = "editor.camera.move_forward";
    /// 1D axis: ascend (+) / descend (−) in flythrough mode.
    pub const MOVE_UP: &str = "editor.camera.move_up";

    /// Button: switch the transform gizmo to Translate mode (orbit camera only).
    pub const GIZMO_TRANSLATE: &str = "editor.gizmo.translate";
    /// Button: switch the transform gizmo to Rotate mode (orbit camera only).
    pub const GIZMO_ROTATE: &str = "editor.gizmo.rotate";
    /// Button: switch the transform gizmo to Scale mode (orbit camera only).
    pub const GIZMO_SCALE: &str = "editor.gizmo.scale";
}

/// Registers the editor's default action set on `actions`. Defaults follow
/// the DCC-tools convention (WASD + QE, Shift sprint, F toggles the
/// flythrough camera) so users coming from Blender / Maya / Unity feel at
/// home.
///
/// Idempotent on the action names — re-registering preserves any user-
/// remapped current bindings while refreshing the defaults that the
/// "Restore defaults" button reverts to.
pub fn register_defaults(actions: &mut ActionMap) {
    actions
        .register(action::TOGGLE_FLYTHROUGH, [Binding::Key(KeyCode::KeyF)])
        .register(
            action::SPRINT,
            [
                Binding::Key(KeyCode::ShiftLeft),
                Binding::Key(KeyCode::ShiftRight),
            ],
        )
        .register_axis(
            action::MOVE_RIGHT,
            AxisBinding::new([Binding::Key(KeyCode::KeyD)], [Binding::Key(KeyCode::KeyA)]),
        )
        .register_axis(
            action::MOVE_FORWARD,
            AxisBinding::new([Binding::Key(KeyCode::KeyW)], [Binding::Key(KeyCode::KeyS)]),
        )
        .register_axis(
            action::MOVE_UP,
            AxisBinding::new([Binding::Key(KeyCode::KeyE)], [Binding::Key(KeyCode::KeyQ)]),
        )
        // Gizmo tool hotkeys (W/E/R). These share physical keys with
        // flythrough movement, so the host gates them on the camera being
        // in orbit mode — in flythrough W/E move the camera, in orbit
        // they switch the gizmo tool.
        .register(action::GIZMO_TRANSLATE, [Binding::Key(KeyCode::KeyW)])
        .register(action::GIZMO_ROTATE, [Binding::Key(KeyCode::KeyE)])
        .register(action::GIZMO_SCALE, [Binding::Key(KeyCode::KeyR)]);
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn defaults_register_every_advertised_action() {
        let mut actions = ActionMap::new();
        register_defaults(&mut actions);

        assert!(actions.has(action::TOGGLE_FLYTHROUGH));
        assert!(actions.has(action::SPRINT));
        assert!(actions.has_axis(action::MOVE_RIGHT));
        assert!(actions.has_axis(action::MOVE_FORWARD));
        assert!(actions.has_axis(action::MOVE_UP));
    }

    #[test]
    fn defaults_are_idempotent_and_preserve_remaps() {
        let mut actions = ActionMap::new();
        register_defaults(&mut actions);

        // User remaps Toggle to Tab.
        actions.set_bindings(action::TOGGLE_FLYTHROUGH, vec![Binding::Key(KeyCode::Tab)]);

        // Re-running register_defaults must not stomp the user's remap.
        register_defaults(&mut actions);
        assert_eq!(
            actions.bindings(action::TOGGLE_FLYTHROUGH),
            &[Binding::Key(KeyCode::Tab)]
        );
        // But the defaults — what "Restore defaults" reverts to — are still F.
        assert_eq!(
            actions.defaults(action::TOGGLE_FLYTHROUGH),
            &[Binding::Key(KeyCode::KeyF)]
        );
    }
}
