use bevy::math::Rect;
use bevy::prelude::*;
use bevy_egui::EguiContexts;
use transform_gizmo_bevy::prelude::{Color32, GizmoMode, GizmoOptions, GizmoOrientation};

use crate::ui::{viewport_keyboard_shortcuts_active, UiState};
use crate::viewport::{ViewportDisplayMode, ViewportSettings};

#[derive(Resource, Default, Debug, Clone, Copy, PartialEq, Eq)]
pub enum EditorGizmoMode {
    #[default]
    Translate,
    Rotate,
    Scale,
}

#[derive(Resource, Default, Debug, Clone, Copy, PartialEq, Eq)]
pub enum EditorGizmoSpace {
    #[default]
    World,
    Local,
}

pub struct EditorGizmoPlugin;

impl Plugin for EditorGizmoPlugin {
    fn build(&self, app: &mut App) {
        app.init_resource::<EditorGizmoMode>()
            .init_resource::<EditorGizmoSpace>()
            .add_systems(Startup, configure_gizmo_visuals)
            .add_systems(
                Update,
                (
                    handle_gizmo_hotkeys,
                    sync_gizmo_viewport_rect,
                    sync_gizmo_options,
                )
                    .chain(),
            );
    }
}

fn configure_gizmo_visuals(mut options: ResMut<GizmoOptions>) {
    options.visuals.x_color = Color32::from_rgb(232, 83, 105);
    options.visuals.y_color = Color32::from_rgb(91, 201, 130);
    options.visuals.z_color = Color32::from_rgb(77, 147, 235);
    options.visuals.s_color = Color32::from_rgb(221, 225, 230);
    options.visuals.highlight_color = Some(Color32::from_rgb(248, 206, 105));
    options.visuals.inactive_alpha = 0.9;
    options.visuals.highlight_alpha = 1.0;
    options.visuals.stroke_width = 3.0;
    options.visuals.gizmo_size = 92.0;
    options.group_targets = true;
}

fn handle_gizmo_hotkeys(
    keys: Res<ButtonInput<KeyCode>>,
    buttons: Res<ButtonInput<MouseButton>>,
    mut contexts: EguiContexts,
    ui_state: Res<UiState>,
    display: Res<ViewportDisplayMode>,
    mut mode: ResMut<EditorGizmoMode>,
    mut space: ResMut<EditorGizmoSpace>,
) -> Result {
    if display.clean_game_view {
        return Ok(());
    }
    let ctx = contexts.ctx_mut()?;
    if !viewport_keyboard_shortcuts_active(&ui_state, ctx, &buttons) {
        return Ok(());
    }
    if keys.just_pressed(KeyCode::KeyW) {
        *mode = EditorGizmoMode::Translate;
    }
    if keys.just_pressed(KeyCode::KeyE) {
        *mode = EditorGizmoMode::Rotate;
    }
    if keys.just_pressed(KeyCode::KeyR) {
        *mode = EditorGizmoMode::Scale;
    }
    if keys.just_pressed(KeyCode::KeyX) {
        *space = match *space {
            EditorGizmoSpace::World => EditorGizmoSpace::Local,
            EditorGizmoSpace::Local => EditorGizmoSpace::World,
        };
    }
    Ok(())
}

/// Maps the viewport egui panel to gizmo cursor space (required for render-to-texture).
fn sync_gizmo_viewport_rect(
    ui_state: Res<UiState>,
    display: Res<ViewportDisplayMode>,
    mut options: ResMut<GizmoOptions>,
) {
    if display.clean_game_view {
        options.viewport_rect = None;
        return;
    }
    let panel = ui_state.viewport_rect;
    options.viewport_rect = if panel.width() > 1.0 && panel.height() > 1.0 {
        Some(Rect {
            min: Vec2::new(panel.min.x, panel.min.y),
            max: Vec2::new(panel.max.x, panel.max.y),
        })
    } else {
        None
    };
}

fn sync_gizmo_options(
    mode: Res<EditorGizmoMode>,
    space: Res<EditorGizmoSpace>,
    settings: Res<ViewportSettings>,
    mut options: ResMut<GizmoOptions>,
) {
    if !mode.is_changed() && !space.is_changed() && !settings.is_changed() {
        return;
    }

    options.gizmo_modes = match *mode {
        EditorGizmoMode::Translate => GizmoMode::all_translate(),
        EditorGizmoMode::Rotate => GizmoMode::all_rotate(),
        EditorGizmoMode::Scale => GizmoMode::all_scale(),
    };
    options.gizmo_orientation = match *space {
        EditorGizmoSpace::World => GizmoOrientation::Global,
        EditorGizmoSpace::Local => GizmoOrientation::Local,
    };
    options.snapping = settings.snap_translate;
    options.snap_distance = settings.snap_increment.max(0.01);
    options.mode_override = None;
}
