//! Persistent gizmo runtime state shared with the engine.
//!
//! [`Gizmos`] groups the active mode and any in-progress drag so apps and the
//! overlay system can interact through `world.resources.user_interface.gizmos`.

#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum GizmoMode {
    #[default]
    LocalTranslation,
    GlobalTranslation,
    Rotation,
    Scale,
    /// All handles drawn at once, oriented to the entity's local axes.
    CompositeLocal,
    /// All handles drawn at once, oriented to world axes.
    CompositeGlobal,
}

#[derive(Clone, Copy, Debug)]
pub struct GizmoTranslationDrag {
    pub axis: u8,
    pub initial_translation: nalgebra_glm::Vec3,
    pub axis_world_direction: nalgebra_glm::Vec3,
    pub axis_world_length: f32,
    pub initial_t: f32,
}

#[derive(Clone, Copy, Debug)]
pub struct GizmoScaleDrag {
    pub axis: u8,
    pub initial_scale: nalgebra_glm::Vec3,
    pub axis_world_length: f32,
    pub initial_t: f32,
}

#[derive(Clone, Copy, Debug)]
pub struct GizmoRotationDrag {
    pub axis: u8,
    pub axis_world: nalgebra_glm::Vec3,
    pub initial_rotation: nalgebra_glm::Quat,
    pub start_world_vector: nalgebra_glm::Vec3,
}

/// In-progress planar scale drag (scaling two components together via a
/// plane handle).
#[derive(Clone, Copy, Debug)]
pub struct GizmoPlanarScaleDrag {
    pub locked_axis: u8,
    pub initial_scale: nalgebra_glm::Vec3,
    pub diagonal_world: nalgebra_glm::Vec3,
    pub diagonal_world_length: f32,
    pub initial_t: f32,
}

/// In-progress planar translation drag (moving freely within a plane).
#[derive(Clone, Copy, Debug)]
pub struct GizmoPlanarTranslationDrag {
    pub locked_axis: u8,
    pub initial_translation: nalgebra_glm::Vec3,
    pub plane_normal_world: nalgebra_glm::Vec3,
    pub initial_world_hit: nalgebra_glm::Vec3,
}

#[derive(Clone)]
pub struct Gizmos {
    pub mode: GizmoMode,
    pub translation_drag: Option<GizmoTranslationDrag>,
    pub scale_drag: Option<GizmoScaleDrag>,
    pub rotation_drag: Option<GizmoRotationDrag>,
    pub planar_scale_drag: Option<GizmoPlanarScaleDrag>,
    pub planar_translation_drag: Option<GizmoPlanarTranslationDrag>,
    pub current_clip: Option<crate::ecs::ui::types::Rect>,
    pub selection_marquee: Option<crate::ecs::ui::types::Rect>,
    pub nav_gizmo_enabled: bool,
    pub nav_gizmo_drag: Option<NavGizmoDrag>,
    pub translation_snap: Option<f32>,
    pub rotation_snap_radians: Option<f32>,
    pub scale_snap: Option<f32>,
}

#[derive(Debug, Clone, Copy)]
pub struct NavGizmoDrag {
    pub press_position: nalgebra_glm::Vec2,
    pub pressed_disc: Option<NavGizmoDisc>,
    pub did_drag: bool,
}

#[derive(Debug, Clone, Copy)]
pub struct NavGizmoDisc {
    pub axis_index: u8,
    pub is_positive: bool,
}

impl Default for Gizmos {
    fn default() -> Self {
        Self {
            mode: GizmoMode::default(),
            translation_drag: None,
            scale_drag: None,
            rotation_drag: None,
            planar_scale_drag: None,
            planar_translation_drag: None,
            current_clip: None,
            selection_marquee: None,
            nav_gizmo_enabled: true,
            nav_gizmo_drag: None,
            translation_snap: None,
            rotation_snap_radians: None,
            scale_snap: None,
        }
    }
}

pub fn snap_value(value: f32, step: f32) -> f32 {
    if step <= 0.0 {
        return value;
    }
    (value / step).round() * step
}

pub fn snap_vec3(value: nalgebra_glm::Vec3, step: f32) -> nalgebra_glm::Vec3 {
    nalgebra_glm::vec3(
        snap_value(value.x, step),
        snap_value(value.y, step),
        snap_value(value.z, step),
    )
}
