#[cfg(not(target_arch = "wasm32"))]
use crate::systems::kenney_assets::KenneyBrowser;
use crate::systems::loading::PendingImport;
use crate::systems::polyhaven::PolyhavenBrowser;
use crate::systems::sample_assets::SampleBrowser;
#[cfg(not(target_arch = "wasm32"))]
use crate::systems::sketchfab::SketchfabBrowser;
use nightshade::ecs::scene::{AssetUuid, Scene};
use nightshade::prelude::{Atmosphere, Entity, SharedTextureQueue, Vec2, Vec3};
use std::collections::HashMap;

#[derive(Clone, Copy)]
pub struct ThumbnailSlot {
    pub layer: u32,
    pub uv_min: Vec2,
    pub uv_max: Vec2,
    pub aspect: Vec2,
}

#[derive(Default)]
pub struct PrefabInstanceLinks {
    /// Live entity → original uuid inside the source `.nsprefab`. Set
    /// when an instance is spawned and used by refresh-from-source to
    /// pair live entities with their source `SceneEntity` for override
    /// diffing.
    pub entity_to_source: HashMap<Entity, AssetUuid>,
    /// In-memory copy of every prefab `Scene` the user has imported in
    /// this session, keyed by `header.prefab_id`. Refresh-from-source
    /// uses this as the baseline so the editor doesn't need to touch
    /// disk again after the initial import.
    pub imported_scenes: HashMap<AssetUuid, Scene>,
}

pub struct LoadingState {
    pub model_entities: Vec<Entity>,
    /// Roots spawned by the dev tools menu (lines, mesh stress test,
    /// 3D text, text lattice). Tracked separately from model_entities
    /// so `clear_scene` can drop them without affecting the loaded
    /// glTF tree.
    pub dev_tool_entities: Vec<Entity>,
    pub loaded: bool,
    pub pending_imports: Vec<PendingImport>,
    pub pending_fit_frames: u32,
    pub pending_fit_roots: Vec<Entity>,
    pub current_model_name: Option<String>,
    pub last_dropped_error: Option<String>,
    pub viewer_mode: bool,
    /// Entities the editor temporarily hid for a prefab thumbnail
    /// capture. Restored after the render frame has consumed the
    /// screenshot command, which takes one render pass.
    pub pending_thumbnail_restore: Vec<Entity>,
    pub pending_thumbnail_restore_frames: u32,
}

impl Default for LoadingState {
    fn default() -> Self {
        Self {
            model_entities: Vec::new(),
            dev_tool_entities: Vec::new(),
            loaded: false,
            pending_imports: Vec::new(),
            pending_fit_frames: 0,
            pending_fit_roots: Vec::new(),
            current_model_name: None,
            last_dropped_error: None,
            viewer_mode: true,
            pending_thumbnail_restore: Vec::new(),
            pending_thumbnail_restore_frames: 0,
        }
    }
}

pub struct CameraState {
    pub camera_entity: Option<Entity>,
    pub reset_camera_on_load: bool,
    pub reset_camera_was_pressed: bool,
    pub fly_mode: bool,
    pub fly_toggle_was_pressed: bool,
}

impl Default for CameraState {
    fn default() -> Self {
        Self {
            camera_entity: None,
            reset_camera_on_load: true,
            reset_camera_was_pressed: false,
            fly_mode: false,
            fly_toggle_was_pressed: false,
        }
    }
}

pub struct SunState {
    pub sun_entity: Option<Entity>,
    pub day_night_hour: f32,
    pub last_ibl_hour: f32,
    pub previous_atmosphere: Atmosphere,
    pub left_arrow_was_pressed: bool,
    pub right_arrow_was_pressed: bool,
    pub auto_cycle: bool,
}

impl Default for SunState {
    fn default() -> Self {
        Self {
            sun_entity: None,
            day_night_hour: 12.0,
            last_ibl_hour: 12.0,
            previous_atmosphere: Atmosphere::Sky,
            left_arrow_was_pressed: false,
            right_arrow_was_pressed: false,
            auto_cycle: true,
        }
    }
}

#[derive(Default)]
pub struct RotationState {
    pub rotation_speed: f32,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RandomKind {
    Model,
    Hdri,
    Both,
    HdriAfterModel,
}

pub struct BrowserState {
    pub sample_browser: SampleBrowser,
    pub polyhaven_browser: PolyhavenBrowser,
    #[cfg(not(target_arch = "wasm32"))]
    pub sketchfab_browser: SketchfabBrowser,
    #[cfg(not(target_arch = "wasm32"))]
    pub kenney_browser: KenneyBrowser,
    pub random_resolution: u32,
    pub pending_random: Option<RandomKind>,
    pub thumbnail_queue: SharedTextureQueue,
    pub thumbnail_signature: u64,
    pub thumbnail_slots: HashMap<String, ThumbnailSlot>,
}

impl Default for BrowserState {
    fn default() -> Self {
        Self {
            sample_browser: SampleBrowser::default(),
            polyhaven_browser: PolyhavenBrowser::default(),
            #[cfg(not(target_arch = "wasm32"))]
            sketchfab_browser: SketchfabBrowser::default(),
            #[cfg(not(target_arch = "wasm32"))]
            kenney_browser: KenneyBrowser::default(),
            random_resolution: 2,
            pending_random: None,
            thumbnail_queue: nightshade::prelude::create_shared_queue(),
            thumbnail_signature: 0,
            thumbnail_slots: HashMap::new(),
        }
    }
}

#[derive(Default)]
pub struct UiState {
    pub show_tree: bool,
    pub show_inspector: bool,
    pub show_fps_label: bool,
    pub selected_entity: Option<Entity>,
    pub selected_entities: Vec<Entity>,
}

#[derive(Default)]
pub struct RepaintState {
    pub last_primary_selection: Option<Entity>,
    pub last_selection_count: usize,
    pub last_visibility_hash: u64,
}

#[derive(Default)]
pub struct PickingState {
    pub press_position: Option<nightshade::prelude::Vec2>,
    pub is_dragging: bool,
    pub last_pick_leaf: Option<Entity>,
    pub last_pick_root: Option<Entity>,
    pub cycle_depth: usize,
    pub pending_alt: bool,
    pub pending_shift: bool,
    pub select_all_was_pressed: bool,
    pub deselect_was_pressed: bool,
}

#[derive(Default)]
pub struct LightGizmoState {
    pub entity: Option<Entity>,
}

/// Tracks the world-space camera gizmos: one shared lines entity for all
/// frustums, plus an invisible-ish pickable proxy mesh per camera so a click on
/// a camera visual selects the camera entity it stands in for.
#[derive(Default)]
pub struct CameraGizmoState {
    pub lines: Option<Entity>,
    pub camera_to_proxy: std::collections::HashMap<Entity, Entity>,
}

impl CameraGizmoState {
    /// Returns the camera a proxy mesh stands in for, if `proxy` is one.
    pub fn camera_for_proxy(&self, proxy: Entity) -> Option<Entity> {
        self.camera_to_proxy
            .iter()
            .find(|(_, value)| **value == proxy)
            .map(|(camera, _)| *camera)
    }
}

/// In-editor authoring state for a camera-path cutscene. Each shot has a
/// draggable marker entity (edited with the normal transform gizmo); a system
/// reads the markers back into the shots and tessellates the catmull-rom curve.
#[derive(Default)]
pub struct CutsceneEditState {
    pub shots: Vec<nightshade::ecs::cutscene::CutsceneShot>,
    pub waypoints: Vec<Entity>,
    pub curve_lines: Option<Entity>,
    pub preview_camera: Option<Entity>,
    pub previewing: bool,
    pub restore_camera: Option<Entity>,
}

pub struct SkeletonDebugState {
    pub enabled: bool,
    pub entity: Option<Entity>,
    pub bone_color: nightshade::prelude::Vec4,
    pub joint_color: nightshade::prelude::Vec4,
    pub joint_size: f32,
}

impl Default for SkeletonDebugState {
    fn default() -> Self {
        Self {
            enabled: false,
            entity: None,
            bone_color: nightshade::prelude::vec4(1.0, 0.65, 0.0, 1.0),
            joint_color: nightshade::prelude::vec4(0.2, 0.7, 1.0, 1.0),
            joint_size: 0.04,
        }
    }
}

pub struct SnapState {
    pub enabled: bool,
    pub translation_step: f32,
    pub rotation_step_degrees: f32,
    pub scale_step: f32,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EditorMode {
    Object,
    Edit { target: Entity },
}

pub struct ModeState {
    pub mode: EditorMode,
    pub tab_was_pressed: bool,
}

impl Default for ModeState {
    fn default() -> Self {
        Self {
            mode: EditorMode::Object,
            tab_was_pressed: false,
        }
    }
}

impl Default for SnapState {
    fn default() -> Self {
        Self {
            enabled: false,
            translation_step: 1.0,
            rotation_step_degrees: 15.0,
            scale_step: 0.1,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GrabAxis {
    X,
    Y,
    Z,
}

impl GrabAxis {
    pub fn unit(self) -> Vec3 {
        match self {
            GrabAxis::X => Vec3::new(1.0, 0.0, 0.0),
            GrabAxis::Y => Vec3::new(0.0, 1.0, 0.0),
            GrabAxis::Z => Vec3::new(0.0, 0.0, 1.0),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GrabConstraint {
    Free,
    Axis(GrabAxis),
    Plane(GrabAxis),
}

#[derive(Clone)]
pub struct GrabEntry {
    pub entity: Entity,
    pub original_local_translation: Vec3,
    pub reference_world_translation: Vec3,
}

#[derive(Clone)]
pub struct GrabSession {
    pub entries: Vec<GrabEntry>,
    pub primary_world_origin: Vec3,
    pub initial_world_hit: Vec3,
    pub plane_normal_world: Vec3,
    pub camera_entity: Entity,
    pub constraint: GrabConstraint,
}

#[derive(Default)]
pub struct GrabState {
    pub session: Option<GrabSession>,
    pub g_was_pressed: bool,
    pub x_was_pressed: bool,
    pub y_was_pressed: bool,
    pub z_was_pressed: bool,
    pub enter_was_pressed: bool,
    pub escape_was_pressed: bool,
    pub left_mouse_was_pressed: bool,
    pub right_mouse_was_pressed: bool,
}

#[derive(Clone)]
pub struct PushPullSession {
    pub entity: Entity,
    pub uuid: AssetUuid,
    pub axis: usize,
    pub sign: f32,
    pub fixed_face: f32,
    pub unit_half: f32,
    pub plane_point: Vec3,
    pub plane_normal: Vec3,
    pub start_face: f32,
    pub original_local: nightshade::prelude::LocalTransform,
}

#[derive(Default)]
pub struct PushPullState {
    pub active: bool,
    pub session: Option<PushPullSession>,
}

/// First-person play-test mode: drop a player into the level and run around.
#[derive(Default)]
pub struct PlayState {
    pub active: bool,
    pub player: Option<Entity>,
    pub camera: Option<Entity>,
    pub ground: Option<Entity>,
    pub restore_camera: Option<Entity>,
    pub escape_was_pressed: bool,
}

pub struct GenerationSettings {
    pub footprint_x: f32,
    pub footprint_z: f32,
    pub stories: u32,
    pub story_height: f32,
    pub wall_thickness: f32,
    pub floor_thickness: f32,
    pub window_spacing: f32,
    pub seed: u32,
    pub variation: f32,
}

impl Default for GenerationSettings {
    fn default() -> Self {
        Self {
            footprint_x: 12.0,
            footprint_z: 9.0,
            stories: 3,
            story_height: 3.2,
            wall_thickness: 0.3,
            floor_thickness: 0.3,
            window_spacing: 3.0,
            seed: 1,
            variation: 0.5,
        }
    }
}

/// General level-authoring state for the `build` tools: the active material
/// palette and the work-plane grid entity. Not greybox-specific.
#[derive(Default)]
pub struct BuildState {
    pub active_palette: usize,
    pub textures_loaded: bool,
    pub grid_lines: Option<Entity>,
}

/// Greybox-only state: whether greybox mode is on and whether brushes are
/// showing their final art.
#[derive(Default)]
pub struct GreyboxState {
    pub enabled: bool,
    pub show_final: bool,
}

#[derive(Clone, Copy, PartialEq, Eq)]
pub enum PlacementShape {
    Cube,
    Plane,
    Cylinder,
    Cone,
    Sphere,
    Torus,
}

/// The draw-on-grid gesture state. `Hover` previews a stamp under the cursor;
/// dragging out a footprint moves to `Footprint`, then releasing moves to
/// `Height` where mouse movement sets the box height before a click commits.
#[derive(Clone, Copy)]
pub enum PlacementPhase {
    Hover,
    Footprint {
        start_corner: Vec3,
    },
    Height {
        footprint_min: Vec3,
        footprint_max: Vec3,
        height: f32,
    },
}

pub struct PlacementState {
    pub active: bool,
    pub shape: PlacementShape,
    pub size: Vec3,
    pub orient_to_normal: bool,
    pub ghost_entity: Option<Entity>,
    pub ghost_shape: Option<PlacementShape>,
    pub phase: PlacementPhase,
    pub press_screen: Option<Vec2>,
}

impl Default for PlacementState {
    fn default() -> Self {
        Self {
            active: false,
            shape: PlacementShape::Cube,
            size: Vec3::new(1.0, 1.0, 1.0),
            orient_to_normal: false,
            ghost_entity: None,
            ghost_shape: None,
            phase: PlacementPhase::Hover,
            press_screen: None,
        }
    }
}

#[derive(Default)]
pub struct ClothSponzaState {
    pub converted: Vec<Entity>,
}
