#![windows_subsystem = "windows"]

extern crate alloc;

mod project;
#[cfg(not(target_arch = "wasm32"))]
mod settings;
#[cfg(not(target_arch = "wasm32"))]
mod toast;
#[cfg(not(target_arch = "wasm32"))]
use toast::ToastLevel;
mod code_editor;
mod theme;
mod views;
#[cfg(not(target_arch = "wasm32"))]
mod webview_manager;
mod widgets;

use nightshade::ecs::generational_registry::registry_entry_by_name;
use nightshade::ecs::graphics::{DepthOfField, DepthOfFieldQuality};
use nightshade::ecs::prefab::resources::mesh_cache_insert;
use nightshade::prelude::*;
use views::*;
use widgets::{EditorWidget, MaterialsWidget, Pane};

const DEFAULT_IBL_HDR: &[u8] = include_bytes!("../assets/moonrise.hdr");

#[derive(Clone, Copy, PartialEq)]
enum CameraView {
    Front,
    Right,
    Top,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    launch(Editor::default())?;
    Ok(())
}

#[derive(Default)]
struct Editor {
    context: EditorContext,
    inspector_ui: ComponentInspectorUi,
    tree_cache: views::TreeCache,
    #[cfg(not(target_arch = "wasm32"))]
    toast_service: toast::ToastService,
    #[cfg(not(target_arch = "wasm32"))]
    settings: settings::EditorSettings,
    project: Option<project::EditorProject>,
    project_state: project::ProjectState,
    theme_state: theme::ThemeState,
}

impl State for Editor {
    fn title(&self) -> &str {
        "Nightshade Editor"
    }

    fn initialize(&mut self, world: &mut World) {
        world.resources.user_interface.enabled = true;
        world.resources.graphics.show_grid = true;
        world.resources.graphics.atmosphere = Atmosphere::Sky;
        world.resources.graphics.selection_outline_enabled = true;

        load_hdr_skybox(world, DEFAULT_IBL_HDR.to_vec());

        self.context.show_left_panel = true;
        self.context.show_right_panel = true;

        let focus = Vec3::zeros();
        let radius = 10.0;
        let yaw = 0.5;
        let pitch = 0.4;
        let main_camera = nightshade::ecs::camera::spawn_pan_orbit_camera(
            world,
            focus,
            radius,
            yaw,
            pitch,
            "Main Camera".to_string(),
        );
        world.resources.active_camera = Some(main_camera);

        spawn_sun(world);

        self.context.tile_tree = Some(Self::create_default_tile_tree());

        #[cfg(not(target_arch = "wasm32"))]
        {
            self.settings = settings::EditorSettings::load();
            if let Some(theme_name) = &self.settings.theme_name
                && let Some((index, preset)) = self
                    .theme_state
                    .presets
                    .iter()
                    .enumerate()
                    .find(|(_, preset)| &preset.name == theme_name)
            {
                self.theme_state.current_config = preset.clone();
                self.theme_state.selected_preset_index = Some(index);
            }
            if let Err(error) = self.settings.save() {
                tracing::error!("Failed to save initial settings: {error}");
            }
            if let Some(startup_project) = self.settings.startup_project.clone() {
                let path = std::path::PathBuf::from(&startup_project);
                if path.exists() {
                    self.load_project_from_path(world, &path);
                } else {
                    tracing::warn!("Startup project does not exist: {}", startup_project);
                }
            }
        }
    }

    fn ui(&mut self, world: &mut World, ui_context: &egui::Context) {
        #[cfg(target_arch = "wasm32")]
        self.process_pending_project_load(world);

        if self.context.play_mode {
            self.play_mode_top_panel_ui(world, ui_context);
            ui_context.memory_mut(|mem| mem.surrender_focus(egui::Id::NULL));
            return;
        }

        ui_context.set_visuals(theme::get_active_theme_visuals(&self.theme_state));
        self.theme_state.preview_theme_index = None;

        #[cfg(not(target_arch = "wasm32"))]
        self.update_window_title(world);

        self.top_panel_ui(world, ui_context);

        if self.context.show_left_panel {
            self.left_panel_ui(world, ui_context);
        }

        if self.context.show_right_panel {
            self.right_panel_ui(world, ui_context);
        }

        self.context.tile_behavior.viewport_rects.clear();
        #[cfg(not(target_arch = "wasm32"))]
        self.context.tile_behavior.web_widget_rects.clear();
        self.context.tile_behavior.viewport_textures =
            world.resources.user_interface.viewport_textures.clone();

        let mut required_cameras: Vec<Entity> = Vec::new();
        if let Some(tree) = &self.context.tile_tree {
            for (_tile_id, tile) in tree.tiles.iter() {
                if let nightshade::prelude::egui_tiles::Tile::Pane(pane) = tile
                    && let Some(camera_entity) = pane.widget.required_camera(world)
                    && !required_cameras.contains(&camera_entity)
                {
                    required_cameras.push(camera_entity);
                }
            }
        }
        world.resources.user_interface.required_cameras = required_cameras;

        self.context.tile_behavior.world = Some(world as *mut World);

        let mut tree = self.context.tile_tree.take();

        egui::CentralPanel::default()
            .frame(egui::Frame::NONE)
            .show(ui_context, |ui| {
                if let Some(tree) = &mut tree {
                    tree.ui(&mut self.context.tile_behavior, ui);

                    if let Some((tile_id, widget_kind)) =
                        self.context.tile_behavior.add_widget_to.take()
                    {
                        let new_pane = Pane::new(widget_kind);
                        let new_tile_id = tree.tiles.insert_pane(new_pane);

                        if let Some(nightshade::prelude::egui_tiles::Tile::Container(
                            nightshade::prelude::egui_tiles::Container::Tabs(tabs),
                        )) = tree.tiles.get_mut(tile_id)
                        {
                            tabs.add_child(new_tile_id);
                        }
                    }

                    if let Some(material_name) = self.context.pending_material_edit.take() {
                        let mut found_materials_widget = false;
                        for tile_id in tree.tiles.tile_ids().collect::<Vec<_>>() {
                            if let Some(nightshade::prelude::egui_tiles::Tile::Pane(pane)) =
                                tree.tiles.get_mut(tile_id)
                                && let EditorWidget::Materials(materials_widget) = &mut pane.widget
                            {
                                materials_widget.selected_material = Some(material_name.clone());
                                found_materials_widget = true;
                                break;
                            }
                        }

                        if !found_materials_widget {
                            let materials_widget = MaterialsWidget {
                                selected_material: Some(material_name),
                                ..Default::default()
                            };
                            let new_pane = Pane {
                                widget: EditorWidget::Materials(materials_widget),
                            };
                            let new_tile_id = tree.tiles.insert_pane(new_pane);

                            if let Some(root_id) = tree.root()
                                && let Some(nightshade::prelude::egui_tiles::Tile::Container(
                                    container,
                                )) = tree.tiles.get_mut(root_id)
                            {
                                match container {
                                    nightshade::prelude::egui_tiles::Container::Tabs(tabs) => {
                                        tabs.add_child(new_tile_id);
                                    }
                                    nightshade::prelude::egui_tiles::Container::Linear(linear) => {
                                        linear.add_child(new_tile_id);
                                    }
                                    nightshade::prelude::egui_tiles::Container::Grid(grid) => {
                                        grid.add_child(new_tile_id);
                                    }
                                }
                            }
                        }
                    }
                }
            });

        self.context.tile_behavior.world = None;
        self.context.tile_tree = tree;

        #[cfg(not(target_arch = "wasm32"))]
        {
            if let Some(window_handle) = &world.resources.window.handle {
                self.context
                    .webview_manager
                    .set_window(window_handle.clone());
            }

            let scale_factor = ui_context.pixels_per_point();
            let active_ids: std::collections::HashSet<String> = self
                .context
                .tile_behavior
                .web_widget_rects
                .iter()
                .map(|(id, _, _)| id.clone())
                .collect();

            self.context.webview_manager.retain_only(&active_ids);

            for (id, url, rect) in self.context.tile_behavior.web_widget_rects.drain(..) {
                if !self.context.webview_manager.has_webview(&id) {
                    self.context.webview_manager.create_webview(
                        id.clone(),
                        &url,
                        rect,
                        scale_factor,
                    );
                }
                self.context
                    .webview_manager
                    .update_position(&id, rect, scale_factor);
            }

            self.context.webview_manager.ensure_all_visible();

            for url in self.context.webview_manager.pending_new_windows() {
                let web_widget = widgets::WebWidget {
                    url,
                    id: uuid::Uuid::new_v4().to_string(),
                };
                let new_pane = Pane {
                    widget: EditorWidget::Web(web_widget),
                };
                if let Some(tree) = &mut self.context.tile_tree {
                    let new_tile_id = tree.tiles.insert_pane(new_pane);
                    if let Some(root_id) = tree.root()
                        && let Some(nightshade::prelude::egui_tiles::Tile::Container(container)) =
                            tree.tiles.get_mut(root_id)
                    {
                        match container {
                            nightshade::prelude::egui_tiles::Container::Tabs(tabs) => {
                                tabs.add_child(new_tile_id);
                            }
                            nightshade::prelude::egui_tiles::Container::Linear(linear) => {
                                linear.add_child(new_tile_id);
                            }
                            nightshade::prelude::egui_tiles::Container::Grid(grid) => {
                                grid.add_child(new_tile_id);
                            }
                        }
                    }
                }
            }
        }

        self.add_primitive_popup_ui(world, ui_context);
        self.entity_context_menu_ui(world, ui_context);

        if self.context.tile_behavior.layout_modified {
            self.context.tile_behavior.layout_modified = false;
            self.context.layout_is_modified = true;
            self.project_state.is_modified = true;
        }

        if self.context.hovered_file_path.is_some() {
            self.drop_indicator_ui(ui_context);
        }

        self.assets_window_ui(world, ui_context);
        self.animations_window_ui(world, ui_context);
        self.visual_effects_window_ui(world, ui_context);

        theme::render_theme_editor_window(ui_context, &mut self.theme_state);

        #[cfg(not(target_arch = "wasm32"))]
        self.toast_service.show(ui_context);
    }

    fn run_systems(&mut self, world: &mut World) {
        nightshade::ecs::text::systems::sync_text_meshes_system(world);
        nightshade::ecs::navmesh::debug::navmesh_debug_draw_system(world);
        nightshade::ecs::physics::physics_debug_draw_system(world);
        let delta_time = world.resources.window.timing.delta_time;
        update_animation_players(world, delta_time);
        apply_animations(world);
        nightshade::ecs::transform::systems::run_systems(world);

        let mut runtime = std::mem::take(&mut world.resources.script_runtime);
        nightshade::ecs::script::run_scripts_system(world, &mut runtime);
        world.resources.script_runtime = runtime;

        if self.context.play_mode {
            world.resources.graphics.show_grid = false;
            world.resources.graphics.show_selected_bounding_volume = false;
            return;
        }

        nightshade::ecs::camera::pan_orbit_camera_system(world);
        ensure_bounding_volumes(world);
        self.update_modal_transform(world);
        self.update_picking(world);
        self.check_context_menu_trigger(world);
        self.update_gizmo(world);
        world.resources.graphics.bounding_volume_selected_entity = self.context.selected_entity;
        world.resources.graphics.show_selected_bounding_volume = false;
    }

    fn update_render_graph(&mut self, graph: &mut RenderGraph<World>, _world: &World) {
        let _ = graph.set_pass_enabled("compute_grayscale", self.context.grayscale_enabled);
    }

    fn handle_event(&mut self, _world: &mut World, message: &nightshade::prelude::Message) {
        match message {
            nightshade::prelude::Message::Input { event } => {
                tracing::debug!("Input event: {:?}", event);
            }
            nightshade::prelude::Message::App { type_name, .. } => {
                tracing::debug!("App event: {}", type_name);
            }
        }
    }

    fn on_keyboard_input(&mut self, world: &mut World, key_code: KeyCode, key_state: KeyState) {
        if key_state == KeyState::Pressed {
            let ctrl_pressed = world
                .resources
                .input
                .keyboard
                .is_key_pressed(KeyCode::ControlLeft)
                || world
                    .resources
                    .input
                    .keyboard
                    .is_key_pressed(KeyCode::ControlRight);

            let shift_pressed = world
                .resources
                .input
                .keyboard
                .is_key_pressed(KeyCode::ShiftLeft)
                || world
                    .resources
                    .input
                    .keyboard
                    .is_key_pressed(KeyCode::ShiftRight);

            let in_modal_transform =
                self.context.modal_transform.operation != views::TransformOperation::None;

            match key_code {
                KeyCode::Escape => {
                    if self.context.play_mode {
                        self.exit_play_mode(world);
                    } else if in_modal_transform {
                        self.cancel_modal_transform(world);
                    }
                }
                KeyCode::KeyQ => {
                    if !self.context.play_mode && !in_modal_transform {
                        world.resources.window.should_exit = true;
                    }
                }
                KeyCode::KeyZ if ctrl_pressed && !self.context.play_mode => {
                    if let Some(gizmo) = &self.context.gizmo {
                        world.update_parent(gizmo.root, None);
                    }
                    if let Some(result) = self.context.undo_history.undo(world) {
                        self.context.selected_entity = result.select_entity;
                        self.project_state.is_modified = true;
                    }
                }
                KeyCode::KeyY if ctrl_pressed && !self.context.play_mode => {
                    if let Some(gizmo) = &self.context.gizmo {
                        world.update_parent(gizmo.root, None);
                    }
                    if let Some(result) = self.context.undo_history.redo(world) {
                        self.context.selected_entity = result.select_entity;
                        self.project_state.is_modified = true;
                    }
                }
                KeyCode::KeyG if !self.context.play_mode && !ctrl_pressed => {
                    self.start_modal_transform(world, views::TransformOperation::Grab);
                }
                KeyCode::KeyR if !self.context.play_mode && !ctrl_pressed => {
                    self.start_modal_transform(world, views::TransformOperation::Rotate);
                }
                KeyCode::KeyS
                    if !self.context.play_mode && !ctrl_pressed && !in_modal_transform =>
                {
                    self.start_modal_transform(world, views::TransformOperation::Scale);
                }
                KeyCode::KeyX if !self.context.play_mode && !ctrl_pressed => {
                    if in_modal_transform {
                        self.context.modal_transform.constraint = views::TransformConstraint::X;
                    } else {
                        self.delete_selected_entity(world);
                    }
                }
                KeyCode::KeyY if !self.context.play_mode && !ctrl_pressed && in_modal_transform => {
                    self.context.modal_transform.constraint = views::TransformConstraint::Y;
                }
                KeyCode::KeyZ if !self.context.play_mode && !ctrl_pressed && in_modal_transform => {
                    self.context.modal_transform.constraint = views::TransformConstraint::Z;
                }
                KeyCode::Enter if in_modal_transform => {
                    self.confirm_modal_transform(world);
                }
                KeyCode::KeyA
                    if !self.context.play_mode && !ctrl_pressed && !in_modal_transform =>
                {
                    let mouse_pos = world.resources.input.mouse.position;
                    self.context.add_primitive_popup = Some(mouse_pos);
                }
                KeyCode::KeyD
                    if !self.context.play_mode && shift_pressed && !in_modal_transform =>
                {
                    self.duplicate_selected_entity(world);
                }
                KeyCode::Numpad1 if !self.context.play_mode && !in_modal_transform => {
                    self.set_camera_view(world, CameraView::Front);
                }
                KeyCode::Numpad3 if !self.context.play_mode && !in_modal_transform => {
                    self.set_camera_view(world, CameraView::Right);
                }
                KeyCode::Numpad7 if !self.context.play_mode && !in_modal_transform => {
                    self.set_camera_view(world, CameraView::Top);
                }
                KeyCode::Numpad5 if !self.context.play_mode && !in_modal_transform => {
                    self.toggle_camera_orthographic(world);
                }
                _ => {}
            }
        }
    }

    fn configure_render_graph(
        &mut self,
        graph: &mut RenderGraph<World>,
        device: &wgpu::Device,
        surface_format: wgpu::TextureFormat,
        resources: RenderResources,
    ) {
        let (width, height) = (1920, 1080);
        let bloom_width = width / 2;
        let bloom_height = height / 2;

        let bloom_texture = graph
            .add_color_texture("bloom")
            .format(wgpu::TextureFormat::Rgba16Float)
            .size(bloom_width, bloom_height)
            .clear_color(wgpu::Color::BLACK)
            .transient();

        let dof_texture = graph
            .add_color_texture("dof_output")
            .format(wgpu::TextureFormat::Rgba16Float)
            .size(width, height)
            .clear_color(wgpu::Color::BLACK)
            .transient();

        let bloom_pass = passes::BloomPass::new(device, width, height);
        graph
            .pass(Box::new(bloom_pass))
            .read("hdr", resources.scene_color)
            .write("bloom", bloom_texture);

        let dof_pass =
            passes::DepthOfFieldPass::new(device, wgpu::TextureFormat::Rgba16Float, width, height);
        graph
            .pass(Box::new(dof_pass))
            .read("hdr", resources.scene_color)
            .read("depth", resources.depth)
            .write("dof_output", dof_texture);

        let postprocess_pass = passes::PostProcessPass::new(device, surface_format, 0.3);
        graph
            .pass(Box::new(postprocess_pass))
            .read("hdr", dof_texture)
            .read("bloom", bloom_texture)
            .write("output", resources.compute_output);

        let swapchain_blit_pass =
            passes::BlitPass::new(device, surface_format).with_name("swapchain_blit_pass");
        graph
            .pass(Box::new(swapchain_blit_pass))
            .read("input", resources.compute_output)
            .write("output", resources.swapchain);
    }

    fn on_dropped_file(&mut self, world: &mut World, path: &std::path::Path) {
        tracing::info!("Dropped file: {:?}", path);
        let path_str = path.to_string_lossy().to_lowercase();
        if let Some(extension) = path.extension() {
            let ext = extension.to_string_lossy().to_lowercase();
            if ext == "hdr" {
                load_hdr_skybox_from_path(world, path.to_path_buf());
                world.resources.graphics.atmosphere = Atmosphere::Hdr;
                self.context.hdr_skybox_path = Some(path.to_path_buf());
            } else if ext == "gltf" || ext == "glb" {
                self.load_gltf(world, path);
            } else if ext == "fbx" {
                #[cfg(not(target_arch = "wasm32"))]
                self.load_fbx(world, path);
            } else if path_str.ends_with(".project.json") || path_str.ends_with(".project.bin") {
                #[cfg(not(target_arch = "wasm32"))]
                self.load_project_from_path(world, path);
            } else if ext == "json" || ext == "bin" {
                #[cfg(not(target_arch = "wasm32"))]
                self.load_map_from_path(world, path);
            } else {
                tracing::warn!("Unsupported file type: {}", ext);
                #[cfg(not(target_arch = "wasm32"))]
                self.toast_service.show_toast(
                    format!("Unsupported file type: {}", ext),
                    ToastLevel::Warning,
                );
            }
        }
        self.context.hovered_file_path = None;
        self.context.drag_file_type = None;
    }

    fn on_hovered_file(&mut self, _world: &mut World, path: &std::path::Path) {
        tracing::info!("Hovered file: {:?}", path);
        self.context.hovered_file_path = Some(path.to_path_buf());
        let path_str = path.to_string_lossy().to_lowercase();
        if let Some(extension) = path.extension() {
            let ext = extension.to_string_lossy().to_lowercase();
            if ext == "hdr" {
                self.context.drag_file_type = Some("hdr".to_string());
            } else if ext == "gltf" || ext == "glb" {
                self.context.drag_file_type = Some("gltf".to_string());
            } else if ext == "fbx" {
                self.context.drag_file_type = Some("fbx".to_string());
            } else if path_str.ends_with(".project.json") || path_str.ends_with(".project.bin") {
                self.context.drag_file_type = Some("project".to_string());
            } else if ext == "json" || ext == "bin" {
                self.context.drag_file_type = Some("map".to_string());
            } else {
                self.context.drag_file_type = None;
            }
        } else {
            self.context.drag_file_type = None;
        }
    }

    fn on_hovered_file_cancelled(&mut self, _world: &mut World) {
        tracing::info!("Hovered file cancelled");
        self.context.hovered_file_path = None;
        self.context.drag_file_type = None;
    }

    fn on_dropped_file_data(&mut self, world: &mut World, name: &str, data: &[u8]) {
        tracing::info!("Dropped file data: {} ({} bytes)", name, data.len());
        let lower_name = name.to_lowercase();
        if lower_name.ends_with(".gltf") || lower_name.ends_with(".glb") {
            self.load_gltf_from_bytes(world, name, data);
        } else if lower_name.ends_with(".project.json") || lower_name.ends_with(".project.bin") {
            self.load_project_from_bytes(world, name, data);
        } else if lower_name.ends_with(".json") || lower_name.ends_with(".bin") {
            self.load_map_from_bytes(world, name, data);
        } else {
            tracing::warn!("Unsupported file type: {}", name);
        }
    }
}

impl Editor {
    fn toggle_all_scripts(&self, world: &mut World, enabled: bool) {
        let entities: Vec<Entity> = world.query_entities(SCRIPT).collect();
        for entity in entities {
            if let Some(script) = world.get_script_mut(entity) {
                script.enabled = enabled;
            }
        }
    }

    fn enter_play_mode(&mut self, world: &mut World) {
        self.context.play_mode = true;
        self.context.scripts_running = true;
        self.toggle_all_scripts(world, true);

        world.resources.graphics.show_grid = false;
        world.resources.graphics.show_cursor = false;

        if let Some(gizmo) = &self.context.gizmo {
            self.set_gizmo_visible(world, gizmo, false);
        }

        for entity in world.query_entities(NAME | CAMERA) {
            if let Some(name) = world.get_name(entity)
                && name.0 == "Camera"
            {
                world.resources.active_camera = Some(entity);
                break;
            }
        }
    }

    fn exit_play_mode(&mut self, world: &mut World) {
        self.context.play_mode = false;
        self.context.scripts_running = false;
        self.toggle_all_scripts(world, false);

        world.resources.graphics.show_grid = true;
        world.resources.graphics.show_cursor = true;

        for entity in world.query_entities(NAME | CAMERA) {
            if let Some(name) = world.get_name(entity)
                && name.0 == "Main Camera"
            {
                world.resources.active_camera = Some(entity);
                break;
            }
        }
    }

    fn left_panel_ui(&mut self, world: &mut World, ui_context: &egui::Context) {
        egui::SidePanel::left("left_panel")
            .default_width(200.0)
            .show(ui_context, |ui| {
                egui::ScrollArea::vertical()
                    .id_salt("left_panel_scroll")
                    .show(ui, |ui| {
                        ui.collapsing("Scene Tree", |ui| {
                            WorldTreeUi::ui_with_context(
                                world,
                                &mut self.context.selected_entity,
                                &mut self.context.undo_history,
                                &mut self.tree_cache,
                                ui,
                            );
                        });
                        ui.collapsing("History", |ui| {
                            ui.horizontal(|ui| {
                                if ui
                                    .add_enabled(
                                        self.context.undo_history.can_undo(),
                                        egui::Button::new("Undo"),
                                    )
                                    .clicked()
                                    && let Some(result) = self.context.undo_history.undo(world)
                                {
                                    self.context.selected_entity = result.select_entity;
                                    self.project_state.is_modified = true;
                                }
                                if ui
                                    .add_enabled(
                                        self.context.undo_history.can_redo(),
                                        egui::Button::new("Redo"),
                                    )
                                    .clicked()
                                    && let Some(result) = self.context.undo_history.redo(world)
                                {
                                    self.context.selected_entity = result.select_entity;
                                    self.project_state.is_modified = true;
                                }
                            });

                            ui.separator();

                            let undo_stack = self.context.undo_history.undo_stack();
                            let redo_stack = self.context.undo_history.redo_stack();

                            if undo_stack.is_empty() && redo_stack.is_empty() {
                                ui.label("No history");
                            } else {
                                egui::ScrollArea::vertical()
                                    .max_height(200.0)
                                    .id_salt("history_scroll")
                                    .show(ui, |ui| {
                                        for entry in redo_stack.iter().rev() {
                                            ui.label(
                                                egui::RichText::new(&entry.description)
                                                    .color(egui::Color32::from_gray(100)),
                                            );
                                        }

                                        if !redo_stack.is_empty() || !undo_stack.is_empty() {
                                            ui.separator();
                                            ui.label(
                                                egui::RichText::new("--- Current ---")
                                                    .strong()
                                                    .color(egui::Color32::from_rgb(100, 200, 100)),
                                            );
                                            ui.separator();
                                        }

                                        for entry in undo_stack.iter().rev() {
                                            ui.label(&entry.description);
                                        }
                                    });
                            }
                        });
                        ui.collapsing("Maps", |ui| {
                            if let Some(ref project) = self.project {
                                let active_map_name = project.active_map_name.clone();
                                let mut map_names: Vec<String> =
                                    project.maps.keys().cloned().collect();
                                map_names.sort();
                                for map_name in &map_names {
                                    let is_active = active_map_name.as_ref() == Some(map_name);
                                    let display_name = if is_active {
                                        format!("● {}", map_name)
                                    } else {
                                        map_name.clone()
                                    };
                                    if ui.selectable_label(is_active, &display_name).clicked()
                                        && !is_active
                                    {
                                        self.switch_to_map(world, map_name);
                                    }
                                }
                            } else {
                                ui.label("No project loaded");
                            }
                            ui.separator();
                            ui.horizontal(|ui| {
                                if ui.button("+ New").clicked() {
                                    let map_count =
                                        self.project.as_ref().map(|p| p.maps.len()).unwrap_or(0);
                                    let new_map_name = format!("Map {}", map_count + 1);
                                    self.create_new_map(world, new_map_name);
                                }
                                if ui
                                    .add_enabled(
                                        self.project
                                            .as_ref()
                                            .map(|p| p.maps.len() > 1)
                                            .unwrap_or(false),
                                        egui::Button::new("Delete"),
                                    )
                                    .clicked()
                                {
                                    self.delete_current_map(world);
                                }
                            });
                        });
                        ui.collapsing("Prefabs", |ui| {
                            if self.context.tile_behavior.prefabs.is_empty() {
                                ui.label("No prefabs loaded");
                                ui.label("Drop .gltf/.glb files or use Import menu");
                            } else {
                                let mut prefab_to_remove = None;
                                for (index, prefab_metadata) in
                                    self.context.tile_behavior.prefabs.iter().enumerate()
                                {
                                    ui.horizontal(|ui| {
                                        let label = if let Some(source) =
                                            &prefab_metadata.source_path
                                        {
                                            format!("{} ({})", prefab_metadata.prefab.name, source)
                                        } else {
                                            prefab_metadata.prefab.name.clone()
                                        };

                                        if ui.button(&label).clicked() {
                                            spawn_prefab_with_animations(
                                                world,
                                                &prefab_metadata.prefab,
                                                &prefab_metadata.animations,
                                                nalgebra_glm::vec3(0.0, 0.0, 0.0),
                                            );
                                        }
                                        if ui.small_button("x").clicked() {
                                            prefab_to_remove = Some(index);
                                        }
                                    });
                                }
                                if let Some(index) = prefab_to_remove {
                                    self.context.tile_behavior.prefabs.remove(index);
                                }
                            }
                        });
                    });
            });
    }

    fn right_panel_ui(&mut self, world: &mut World, ui_context: &egui::Context) {
        egui::SidePanel::right("right_panel")
            .default_width(250.0)
            .show(ui_context, |ui| {
                ui.heading("Inspector");
                ui.separator();
                egui::ScrollArea::vertical()
                    .id_salt("inspector_scroll")
                    .show(ui, |ui| {
                        ComponentInspectorUi::ui(
                            &mut self.inspector_ui,
                            &mut self.context,
                            world,
                            ui,
                        );
                    });
            });
    }

    #[cfg(not(target_arch = "wasm32"))]
    fn update_window_title(&self, world: &World) {
        let window_title = if let Some(project_path) = &self.context.current_project_path {
            let path_display = project_path.display();
            if self.project_state.is_modified {
                format!("Nightshade Editor - {} (*)", path_display)
            } else {
                format!("Nightshade Editor - {}", path_display)
            }
        } else {
            "Nightshade Editor".to_string()
        };

        if let Some(window_handle) = &world.resources.window.handle {
            window_handle.set_title(&window_title);
        }
    }

    fn play_mode_top_panel_ui(&mut self, world: &mut World, ui_context: &egui::Context) {
        let mut stop_clicked = false;
        let mut pause_clicked = false;

        egui::TopBottomPanel::top("top").show(ui_context, |ui| {
            ui.horizontal(|ui| {
                let status_text = if self.context.scripts_running {
                    egui::RichText::new("▶ PLAYING")
                        .color(egui::Color32::GREEN)
                        .strong()
                } else {
                    egui::RichText::new("⏸ PAUSED")
                        .color(egui::Color32::YELLOW)
                        .strong()
                };
                ui.label(status_text);

                ui.separator();

                let pause_text = if self.context.scripts_running {
                    "⏸ Pause"
                } else {
                    "▶ Resume"
                };
                if ui
                    .add(egui::Button::new(pause_text).sense(egui::Sense::click()))
                    .clicked()
                {
                    pause_clicked = true;
                }
                if ui
                    .add(egui::Button::new("⏹ Stop").sense(egui::Sense::click()))
                    .clicked()
                {
                    stop_clicked = true;
                }

                ui.separator();

                ui.label(egui::RichText::new("Press Esc to stop").weak().small());

                ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
                    ui.label(format!(
                        "FPS: {}",
                        world.resources.window.timing.frames_per_second
                    ));
                });
            });
        });

        if stop_clicked {
            self.exit_play_mode(world);
        }
        if pause_clicked {
            self.context.scripts_running = !self.context.scripts_running;
            self.toggle_all_scripts(world, self.context.scripts_running);
        }
    }

    fn top_panel_ui(&mut self, world: &mut World, ui_context: &egui::Context) {
        egui::TopBottomPanel::top("top").show(ui_context, |ui| {
            ui.horizontal(|ui| {
                egui::MenuBar::new().ui(ui, |ui| {
                    #[cfg(not(target_arch = "wasm32"))]
                    {
                        if self.context.editing_project_name {
                            ui.horizontal(|ui| {
                                ui.add(
                                    egui::TextEdit::singleline(
                                        &mut self.context.project_name_edit_buffer,
                                    )
                                    .desired_width(150.0),
                                );

                                if ui.button("❌").clicked() {
                                    self.context.editing_project_name = false;
                                    self.context.project_name_edit_buffer.clear();
                                }

                                if ui.button("✅").clicked() {
                                    if !self.context.project_name_edit_buffer.trim().is_empty() {
                                        let new_name = self
                                            .context
                                            .project_name_edit_buffer
                                            .trim()
                                            .to_string();
                                        self.context.current_project_name = Some(new_name.clone());
                                        if let Some(ref mut project) = self.project {
                                            project.project_name = Some(new_name);
                                        }
                                        self.project_state.is_modified = true;
                                    }
                                    self.context.editing_project_name = false;
                                    self.context.project_name_edit_buffer.clear();
                                }
                            });
                        } else {
                            self.display_project_menu(world, ui);
                        }

                        ui.separator();

                        if self.context.editing_layout_index.is_some() {
                            ui.horizontal(|ui| {
                                ui.add(
                                    egui::TextEdit::singleline(
                                        &mut self.context.layout_name_edit_buffer,
                                    )
                                    .desired_width(120.0),
                                );

                                if ui.button("❌").clicked() {
                                    self.context.editing_layout_index = None;
                                    self.context.layout_name_edit_buffer.clear();
                                }

                                if ui.button("✅").clicked() {
                                    if !self.context.layout_name_edit_buffer.trim().is_empty()
                                        && let Some(index) = self.context.editing_layout_index
                                        && let Some(ref mut project) = self.project
                                        && let Some(layout) = project.layouts.get_mut(index)
                                    {
                                        let new_name =
                                            self.context.layout_name_edit_buffer.trim().to_string();
                                        layout.name = new_name;
                                        self.project_state.is_modified = true;
                                        self.context.layout_is_modified = true;
                                    }
                                    self.context.editing_layout_index = None;
                                    self.context.layout_name_edit_buffer.clear();
                                }
                            });
                        } else {
                            self.display_layout_menu(ui);
                        }
                    }

                    #[cfg(target_arch = "wasm32")]
                    {
                        let light_green = egui::Color32::from_rgb(144, 238, 144);
                        ui.menu_button(
                            egui::RichText::new("🔨 Project").color(light_green),
                            |ui| {
                                if ui.button("New Project").clicked() {
                                    self.new_project(world);
                                    ui.close();
                                }
                                ui.separator();
                                if ui.button("Save Project...").clicked() {
                                    self.save_project(world);
                                    ui.close();
                                }
                                if ui.button("Load Project...").clicked() {
                                    self.load_project(world);
                                    ui.close();
                                }
                            },
                        );
                    }

                    ui.separator();
                    ui.menu_button("View", |ui| {
                        ui.checkbox(&mut self.context.show_left_panel, "Left Panel");
                        ui.checkbox(&mut self.context.show_right_panel, "Right Panel");
                        if ui.button("Assets...").clicked() {
                            self.context.show_assets_window = true;
                            ui.close();
                        }
                        if ui.button("Animations...").clicked() {
                            self.context.show_animations_window = true;
                            ui.close();
                        }
                        ui.separator();
                        ui.checkbox(&mut world.resources.graphics.show_grid, "Grid");
                        ui.checkbox(
                            &mut world.resources.graphics.show_bounding_volumes,
                            "Bounding Volumes",
                        );
                        ui.checkbox(&mut world.resources.graphics.show_normals, "Normals");
                        if world.resources.graphics.show_normals {
                            ui.horizontal(|ui| {
                                ui.label("Length:");
                                ui.add(
                                    egui::Slider::new(
                                        &mut world.resources.graphics.normal_line_length,
                                        0.01..=1.0,
                                    )
                                    .fixed_decimals(2),
                                );
                            });
                            ui.horizontal(|ui| {
                                ui.label("Color:");
                                ui.color_edit_button_rgba_unmultiplied(
                                    &mut world.resources.graphics.normal_line_color,
                                );
                            });
                        }
                        ui.checkbox(&mut world.resources.navmesh.debug_draw, "NavMesh");
                        ui.checkbox(&mut world.resources.physics.debug_draw, "Colliders");

                        ui.separator();
                        ui.label("Render Layers:");
                        ui.checkbox(
                            &mut world.resources.graphics.render_layer_world_enabled,
                            "World Layer",
                        );
                        ui.checkbox(
                            &mut world.resources.graphics.render_layer_overlay_enabled,
                            "Overlay Layer",
                        );

                        ui.separator();
                        if ui.button("Visual Effects...").clicked() {
                            self.context.show_visual_effects_window = true;
                            ui.close();
                        }

                        ui.separator();
                        ui.menu_button(
                            format!("Atmosphere: {:?}", world.resources.graphics.atmosphere),
                            |ui| {
                                if ui
                                    .selectable_label(
                                        world.resources.graphics.atmosphere == Atmosphere::None,
                                        "None",
                                    )
                                    .clicked()
                                {
                                    world.resources.graphics.atmosphere = Atmosphere::None;
                                    ui.close();
                                }
                                if ui
                                    .selectable_label(
                                        world.resources.graphics.atmosphere == Atmosphere::Sky,
                                        "Sky",
                                    )
                                    .clicked()
                                {
                                    world.resources.graphics.atmosphere = Atmosphere::Sky;
                                    ui.close();
                                }
                                if ui
                                    .selectable_label(
                                        world.resources.graphics.atmosphere
                                            == Atmosphere::CloudySky,
                                        "Cloudy Sky",
                                    )
                                    .clicked()
                                {
                                    world.resources.graphics.atmosphere = Atmosphere::CloudySky;
                                    ui.close();
                                }
                                if ui
                                    .selectable_label(
                                        world.resources.graphics.atmosphere == Atmosphere::Space,
                                        "Space",
                                    )
                                    .clicked()
                                {
                                    world.resources.graphics.atmosphere = Atmosphere::Space;
                                    ui.close();
                                }
                                if ui
                                    .selectable_label(
                                        world.resources.graphics.atmosphere == Atmosphere::Nebula,
                                        "Nebula",
                                    )
                                    .clicked()
                                {
                                    world.resources.graphics.atmosphere = Atmosphere::Nebula;
                                    ui.close();
                                }
                                if ui
                                    .selectable_label(
                                        world.resources.graphics.atmosphere == Atmosphere::Sunset,
                                        "Sunset",
                                    )
                                    .clicked()
                                {
                                    world.resources.graphics.atmosphere = Atmosphere::Sunset;
                                    ui.close();
                                }
                                if ui
                                    .selectable_label(
                                        world.resources.graphics.atmosphere
                                            == Atmosphere::Nighttime,
                                        "Nighttime",
                                    )
                                    .clicked()
                                {
                                    world.resources.graphics.atmosphere = Atmosphere::Nighttime;
                                    ui.close();
                                }
                                if ui
                                    .selectable_label(
                                        world.resources.graphics.atmosphere == Atmosphere::Hdr,
                                        "HDR",
                                    )
                                    .clicked()
                                {
                                    world.resources.graphics.atmosphere = Atmosphere::Hdr;
                                    ui.close();
                                }
                            },
                        );

                        ui.separator();
                        ui.menu_button("Stats", |ui| {
                            ui.label(format!("Entities: {}", world.get_all_entities().len()));
                            let lines = query_number_of_lines(world);
                            ui.label(format!("Lines: {}/{}", lines, MAX_LINES));
                        });

                        ui.separator();
                        if ui.button("Theme Editor").clicked() {
                            self.theme_state.show_theme_editor = true;
                            ui.close();
                        }
                        ui.menu_button(
                            format!("Theme: {}", self.theme_state.current_config.name),
                            |ui| {
                                let mut seen_light_theme = false;
                                for (index, preset) in self.theme_state.presets.iter().enumerate() {
                                    if !preset.dark_mode && !seen_light_theme {
                                        ui.separator();
                                        seen_light_theme = true;
                                    }
                                    let is_selected =
                                        self.theme_state.selected_preset_index == Some(index);
                                    let label = if is_selected {
                                        format!("✔ {}", preset.name)
                                    } else {
                                        format!("    {}", preset.name)
                                    };
                                    let response = ui.selectable_label(false, &label);
                                    if response.hovered() {
                                        self.theme_state.preview_theme_index = Some(index);
                                    }
                                    if response.clicked() {
                                        self.theme_state.current_config = preset.clone();
                                        self.theme_state.selected_preset_index = Some(index);
                                        #[cfg(not(target_arch = "wasm32"))]
                                        {
                                            self.settings.theme_name = Some(preset.name.clone());
                                            if let Err(error) = self.settings.save() {
                                                tracing::error!("Failed to save settings: {error}");
                                            }
                                            self.toast_service.show_toast(
                                                format!("Theme: {}", preset.name),
                                                ToastLevel::Success,
                                            );
                                        }
                                        ui.close();
                                    }
                                }
                            },
                        );

                    });
                    ui.separator();
                    ui.menu_button("Tools", |ui| {
                        if ui.button("Clear Scene").clicked() {
                            self.clear_scene(world);
                            ui.close();
                        }

                        ui.separator();

                        let mut cameras: Vec<Entity> = world.query_entities(CAMERA).collect();
                        cameras.sort_by_key(|entity| entity.id);
                        let camera_count = cameras.len();
                        if ui
                            .add_enabled(
                                camera_count > 0,
                                egui::Button::new(format!("Show All Cameras ({})", camera_count)),
                            )
                            .clicked()
                        {
                            let mut tiles = nightshade::prelude::egui_tiles::Tiles::default();
                            let viewport_ids: Vec<_> = cameras
                                .iter()
                                .enumerate()
                                .map(|(index, _)| tiles.insert_pane(Pane::camera(index)))
                                .collect();

                            let root = if viewport_ids.len() == 1 {
                                viewport_ids[0]
                            } else {
                                tiles.insert_grid_tile(viewport_ids)
                            };

                            self.context.tile_tree =
                                Some(nightshade::prelude::egui_tiles::Tree::new("tile_tree", root, tiles));
                            ui.close();
                        }

                        ui.separator();

                        ui.menu_button("Create", |ui| {
                            ui.set_min_width(150.0);

                            ui.label("Lighting");
                            if ui.button("Sun").clicked() {
                                spawn_sun(world);
                                ui.close();
                            }
                            ui.separator();

                            ui.label("Primitives");
                            if ui.button("Cube").clicked() {
                                spawn_cube_at(world, Vec3::new(0.0, 0.0, 0.0));
                                ui.close();
                            }
                            if ui.button("Sphere").clicked() {
                                spawn_sphere_at(world, Vec3::new(0.0, 0.0, 0.0));
                                ui.close();
                            }
                            if ui.button("Cylinder").clicked() {
                                spawn_cylinder_at(world, Vec3::new(0.0, 0.0, 0.0));
                                ui.close();
                            }
                            if ui.button("Torus").clicked() {
                                spawn_torus_at(world, Vec3::new(0.0, 0.0, 0.0));
                                ui.close();
                            }
                            if ui.button("Cone").clicked() {
                                spawn_cone_at(world, Vec3::new(0.0, 0.0, 0.0));
                                ui.close();
                            }
                            if ui.button("Plane").clicked() {
                                spawn_plane_at(world, Vec3::new(0.0, 0.0, 0.0));
                                ui.close();
                            }
                            ui.separator();

                            ui.label("Special");
                            if ui.button("Water Plane").clicked() {
                                spawn_water_plane_at(world, Vec3::new(0.0, 0.0, 0.0));
                                ui.close();
                            }
                        });

                        #[cfg(not(target_arch = "wasm32"))]
                        {
                            ui.separator();
                            if ui
                                .button("Import FBX")
                                .on_hover_text("Import an FBX file with meshes and animations")
                                .clicked()
                            {
                                self.import_fbx(world);
                                ui.close();
                            }

                            let has_animation_player = self
                                .context
                                .selected_entity
                                .map(|entity| {
                                    world.entity_has_components(
                                        entity,
                                        nightshade::ecs::world::ANIMATION_PLAYER,
                                    )
                                })
                                .unwrap_or(false);

                            if ui
                                .add_enabled(
                                    has_animation_player,
                                    egui::Button::new("Import Animation"),
                                )
                                .on_hover_text(
                                    "Import animations from FBX and add to selected entity's AnimationPlayer",
                                )
                                .on_disabled_hover_text(
                                    "Select an entity with an AnimationPlayer first",
                                )
                                .clicked()
                            {
                                self.import_animation(world);
                                ui.close();
                            }
                        }

                        ui.separator();
                        ui.label("Tests");
                        ui.separator();

                        ui.label("Line Scaling Test:");
                        if ui.button("Spawn 20,000 Lines").clicked() {
                            spawn_lines(world);
                        }
                        ui.separator();
                        ui.label("Mesh Scaling Test:");
                        if ui.button("Spawn 10,000 Meshes").clicked() {
                            spawn_meshes(world);
                        }
                        ui.separator();
                        ui.label("Text Test:");
                        if ui.button("Spawn 3D Text").clicked() {
                            spawn_3d_text(world);
                        }
                        if ui.button("Spawn 5K Text Lattice (Stress Test)").clicked() {
                            spawn_text_lattice(world);
                        }

                        ui.separator();
                        let old_mode = self.context.gizmo_mode;
                        ui.menu_button(format!("Gizmo: {:?}", self.context.gizmo_mode), |ui| {
                            if ui
                                .selectable_label(
                                    self.context.gizmo_mode
                                        == nightshade::ecs::gizmos::GizmoMode::LocalTranslation,
                                    "Local Translation",
                                )
                                .clicked()
                            {
                                self.context.gizmo_mode =
                                    nightshade::ecs::gizmos::GizmoMode::LocalTranslation;
                                ui.close();
                            }
                            if ui
                                .selectable_label(
                                    self.context.gizmo_mode
                                        == nightshade::ecs::gizmos::GizmoMode::GlobalTranslation,
                                    "Global Translation",
                                )
                                .clicked()
                            {
                                self.context.gizmo_mode =
                                    nightshade::ecs::gizmos::GizmoMode::GlobalTranslation;
                                ui.close();
                            }
                            if ui
                                .selectable_label(
                                    self.context.gizmo_mode
                                        == nightshade::ecs::gizmos::GizmoMode::Rotation,
                                    "Rotation",
                                )
                                .clicked()
                            {
                                self.context.gizmo_mode =
                                    nightshade::ecs::gizmos::GizmoMode::Rotation;
                                ui.close();
                            }
                            if ui
                                .selectable_label(
                                    self.context.gizmo_mode
                                        == nightshade::ecs::gizmos::GizmoMode::Scale,
                                    "Scale",
                                )
                                .clicked()
                            {
                                self.context.gizmo_mode = nightshade::ecs::gizmos::GizmoMode::Scale;
                                ui.close();
                            }
                        });
                        if old_mode != self.context.gizmo_mode {
                            self.recreate_gizmo_for_mode(world);
                        }
                    });
                    ui.separator();

                    ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
                        let _ = ui.small_button(
                            egui::RichText::new(format!("🏷 v{}", env!("CARGO_PKG_VERSION")))
                                .color(egui::Color32::from_rgb(255, 145, 0))
                                .strong(),
                        );
                        ui.separator();
                        ui.label(format!(
                            "FPS: {}",
                            world.resources.window.timing.frames_per_second
                        ));
                        ui.separator();
                        if ui
                            .button("▶ Play")
                            .on_hover_text("Enter play mode (Esc to exit)")
                            .clicked()
                        {
                            self.enter_play_mode(world);
                        }
                        ui.separator();

                        let unlit = world.resources.graphics.unlit_mode;
                        if ui
                            .selectable_label(unlit, "Unlit")
                            .on_hover_text("Flat shading without lighting (CAD mode)")
                            .clicked()
                        {
                            world.resources.graphics.unlit_mode = true;
                        }
                        if ui
                            .selectable_label(!unlit, "Lit")
                            .on_hover_text("Full PBR lighting")
                            .clicked()
                        {
                            world.resources.graphics.unlit_mode = false;
                        }
                        ui.separator();
                    });
                });
            });
        });
    }

    #[cfg(not(target_arch = "wasm32"))]
    fn display_project_menu(&mut self, world: &mut World, ui: &mut egui::Ui) {
        let light_green = egui::Color32::from_rgb(144, 238, 144);

        ui.menu_button(
            egui::RichText::new("🔨 Project").color(light_green),
            |ui| {
                if ui.button("New Project").clicked() {
                    self.new_project(world);
                    ui.close();
                }
                ui.separator();
                if ui.button("Save Project").clicked() {
                    self.save_project(world);
                    ui.close();
                }
                if ui.button("Save Project As...").clicked() {
                    self.save_project_as(world);
                    ui.close();
                }
                if ui.button("Collect All and Save...").clicked() {
                    self.collect_all_and_save(world);
                    ui.close();
                }
                if ui.button("Load Project...").clicked() {
                    self.load_project(world);
                    ui.close();
                }
                if ui.button("Load Map...").clicked() {
                    self.load_map_file(world);
                    ui.close();
                }
                ui.separator();

                let current_project_is_startup = if let Some(current_path) =
                    &self.context.current_project_path
                    && let Some(startup_path) = &self.settings.startup_project
                {
                    current_path.to_string_lossy() == *startup_path
                } else {
                    false
                };

                if ui
                    .add_enabled(
                        self.context.current_project_path.is_some() && !current_project_is_startup,
                        egui::Button::new("Set as Startup Project"),
                    )
                    .clicked()
                {
                    self.set_as_startup_project();
                    ui.close();
                }
                if ui
                    .add_enabled(
                        self.settings.startup_project.is_some(),
                        egui::Button::new("Unset Startup Project"),
                    )
                    .clicked()
                {
                    self.unset_startup_project();
                    ui.close();
                }
                ui.separator();

                let has_recent_projects = !self.settings.recent_projects.is_empty();
                ui.label(
                    egui::RichText::new("Recent Projects")
                        .color(egui::Color32::from_rgb(150, 150, 150))
                        .small()
                        .strong(),
                );
                ui.add_space(4.0);
                if !has_recent_projects {
                    ui.label(
                        egui::RichText::new("  No recent projects")
                            .color(egui::Color32::from_rgb(120, 120, 120))
                            .small()
                            .italics(),
                    );
                } else {
                    let recent_projects = self.settings.recent_projects.clone();
                    let startup_project = self.settings.startup_project.clone();
                    for project_path in recent_projects.iter() {
                        let path_string = project_path.clone();
                        let project_name = settings::EditorSettings::get_project_name(&path_string);
                        let is_startup_project = if let Some(startup_path) = &startup_project {
                            &path_string == startup_path
                        } else {
                            false
                        };
                        let display_name = if project_name.trim().is_empty() {
                            if is_startup_project {
                                "⭐ Untitled".to_string()
                            } else {
                                "Untitled".to_string()
                            }
                        } else if is_startup_project {
                            format!("⭐ {}", project_name)
                        } else {
                            project_name
                        };
                        let button = ui
                            .add(
                                egui::Button::new(
                                    egui::RichText::new(display_name)
                                        .color(egui::Color32::from_rgb(100, 200, 255)),
                                )
                                .min_size(egui::vec2(200.0, 0.0)),
                            )
                            .on_hover_text(&path_string);
                        if button.clicked() {
                            self.load_project_from_path(
                                world,
                                &std::path::PathBuf::from(&path_string),
                            );
                            ui.close();
                        }
                    }
                    ui.add_space(4.0);
                    if ui
                        .button(
                            egui::RichText::new("Clear Recent Projects")
                                .color(egui::Color32::from_rgb(255, 100, 100))
                                .small(),
                        )
                        .clicked()
                    {
                        self.settings.clear_recent_projects();
                        if let Err(error) = self.settings.save() {
                            tracing::error!("Failed to save settings: {error}");
                        }
                        ui.close();
                    }
                }
            },
        );

        let project_name = self
            .context
            .current_project_name
            .clone()
            .unwrap_or_else(|| "Untitled".to_string());
        let display_name = if self.project_state.is_modified {
            format!("{} *", project_name)
        } else {
            project_name.clone()
        };

        let text_color = if ui.visuals().dark_mode {
            egui::Color32::from_rgb(246, 247, 245)
        } else {
            egui::Color32::from_rgb(25, 25, 24)
        };
        ui.label(egui::RichText::new(&display_name).color(text_color));

        if ui
            .small_button("✏")
            .on_hover_text("Edit project name")
            .clicked()
        {
            self.context.editing_project_name = true;
            self.context.project_name_edit_buffer = project_name;
        }
    }

    #[cfg(not(target_arch = "wasm32"))]
    fn display_layout_menu(&mut self, ui: &mut egui::Ui) {
        let light_green = egui::Color32::from_rgb(144, 238, 144);

        let layout_name = self
            .project
            .as_ref()
            .and_then(|p| p.active_layout())
            .map(|l| l.name.clone())
            .unwrap_or_else(|| "Default".to_string());
        let layout_count = self.project.as_ref().map(|p| p.layouts.len()).unwrap_or(0);
        let active_layout_index = self
            .project
            .as_ref()
            .map(|p| p.active_layout_index)
            .unwrap_or(0);
        let layouts: Vec<(usize, String, bool)> = self
            .project
            .as_ref()
            .map(|p| {
                p.layouts
                    .iter()
                    .enumerate()
                    .map(|(index, layout)| {
                        (index, layout.name.clone(), p.active_layout_index == index)
                    })
                    .collect()
            })
            .unwrap_or_default();

        let mut switch_to_layout: Option<usize> = None;
        let mut start_editing_layout: Option<(usize, String)> = None;
        let mut create_new = false;
        let mut save_current = false;
        let mut delete_current = false;
        let mut reset_layout = false;

        ui.menu_button(egui::RichText::new("📐 Layout").color(light_green), |ui| {
            if self.project.is_some() {
                if ui.button("Save Layout").clicked() {
                    save_current = true;
                    ui.close();
                }
                if ui.button("New Layout").clicked() {
                    create_new = true;
                    ui.close();
                }
                if ui.button("Reset Layout").clicked() {
                    reset_layout = true;
                    ui.close();
                }
                let can_delete = layout_count > 1;
                if ui
                    .add_enabled(can_delete, egui::Button::new("Delete Layout"))
                    .clicked()
                {
                    delete_current = true;
                    ui.close();
                }
                ui.separator();

                for (index, name, is_active) in &layouts {
                    ui.horizontal(|ui| {
                        let display_name = if *is_active {
                            format!("● {}", name)
                        } else {
                            name.clone()
                        };

                        if ui.selectable_label(*is_active, &display_name).clicked() && !is_active {
                            switch_to_layout = Some(*index);
                        }

                        if ui.small_button("✏").on_hover_text("Rename").clicked() {
                            start_editing_layout = Some((*index, name.clone()));
                        }
                    });
                }
            } else {
                ui.label("No project loaded");
            }
        });

        let layout_display = if self.context.layout_is_modified {
            format!("{} *", layout_name)
        } else {
            layout_name.clone()
        };

        let text_color = if ui.visuals().dark_mode {
            egui::Color32::from_rgb(246, 247, 245)
        } else {
            egui::Color32::from_rgb(25, 25, 24)
        };
        ui.label(egui::RichText::new(&layout_display).color(text_color));

        if ui
            .small_button("✏")
            .on_hover_text("Edit layout name")
            .clicked()
        {
            self.context.editing_layout_index = Some(active_layout_index);
            self.context.layout_name_edit_buffer = layout_name;
        }

        if let Some(index) = switch_to_layout {
            self.switch_to_layout(index);
        }
        if let Some((index, name)) = start_editing_layout {
            self.context.editing_layout_index = Some(index);
            self.context.layout_name_edit_buffer = name;
        }
        if create_new {
            self.create_new_layout();
        }
        if save_current {
            self.save_current_layout();
        }
        if delete_current {
            self.delete_current_layout();
        }
        if reset_layout {
            self.reset_current_layout();
        }
    }

    #[cfg(not(target_arch = "wasm32"))]
    fn reset_current_layout(&mut self) {
        let default_tree = Self::create_default_tile_tree();
        self.context.tile_tree = Some(default_tree.clone());
        if let Some(ref mut project) = self.project
            && let Some(layout) = project.layouts.get_mut(project.active_layout_index)
        {
            layout.tree = default_tree;
            self.project_state.is_modified = true;
            self.context.layout_is_modified = true;
        }
        self.toast_service
            .show_toast("Reset layout to default", ToastLevel::Success);
    }

    fn cleanup_gizmo_entities(&mut self, world: &mut World) {
        let camera_entity = world.resources.active_camera;
        let gizmo_root = self.context.gizmo.as_ref().map(|g| g.root);

        let mut gizmo_descendants: std::collections::HashSet<Entity> =
            std::collections::HashSet::new();
        if let Some(root) = gizmo_root {
            gizmo_descendants.insert(root);
            for descendant in nightshade::ecs::transform::queries::query_descendants(world, root) {
                gizmo_descendants.insert(descendant);
            }
        }

        let all_entities: Vec<Entity> = world.get_all_entities();
        let mut entities_to_remove: Vec<Entity> = Vec::new();

        for entity in all_entities {
            if Some(entity) == camera_entity {
                continue;
            }
            if gizmo_descendants.contains(&entity) {
                continue;
            }

            let should_remove = if let Some(name) = world.get_name(entity) {
                name.0.contains("Gizmo") || name.0.contains("gizmo")
            } else {
                false
            } || if let Some(render_mesh) = world.get_render_mesh(entity) {
                render_mesh.name.contains("Gizmo") || render_mesh.name.contains("gizmo")
            } else {
                false
            };

            if should_remove {
                entities_to_remove.push(entity);
            }
        }

        for entity in entities_to_remove {
            world.queue_command(WorldCommand::DespawnRecursive { entity });
        }
    }

    fn load_gltf(&mut self, world: &mut World, path: &std::path::Path) {
        tracing::info!("Loading GLTF from: {:?}", path);

        let source_path = path
            .canonicalize()
            .ok()
            .and_then(|absolute| absolute.to_str().map(|s| s.to_string()))
            .unwrap_or_else(|| path.display().to_string());

        match nightshade::ecs::prefab::import_gltf_from_path(path) {
            Ok(result) => {
                tracing::info!("Successfully loaded GLTF file");
                tracing::info!("Loaded {} meshes", result.meshes.len());
                tracing::info!("Loaded {} materials", result.materials.len());
                tracing::info!("Loaded {} textures", result.textures.len());
                tracing::info!("Loaded {} prefabs", result.prefabs.len());
                tracing::info!("Loaded {} animations", result.animations.len());

                for (name, (rgba_data, width, height)) in result.textures {
                    tracing::info!("Loading texture '{}': {}x{}", name, width, height);
                    world.queue_command(WorldCommand::LoadTexture {
                        name,
                        rgba_data,
                        width,
                        height,
                    });
                }

                for (name, mesh) in result.meshes {
                    tracing::info!(
                        "Mesh '{}': {} vertices, {} indices",
                        name,
                        mesh.vertices.len(),
                        mesh.indices.len()
                    );
                    mesh_cache_insert(&mut world.resources.mesh_cache, name, mesh);
                }

                let mut last_spawned_entity = None;
                for prefab in &result.prefabs {
                    tracing::info!("Spawning prefab '{}'", prefab.name);
                    let entity = nightshade::ecs::prefab::spawn_prefab_with_source(
                        world,
                        prefab,
                        &result.animations,
                        &result.skins,
                        nalgebra_glm::vec3(0.0, 0.0, 0.0),
                        Some(&source_path),
                    );
                    tracing::info!("Spawned prefab with root entity {:?}", entity);
                    last_spawned_entity = Some(entity);
                }

                if let Some(entity) = last_spawned_entity {
                    self.context.selected_entity = Some(entity);
                }

                for prefab in result.prefabs {
                    self.context.tile_behavior.prefabs.push(PrefabMetadata {
                        prefab,
                        animations: result.animations.clone(),
                        source_path: Some(source_path.clone()),
                    });
                }
            }
            Err(e) => {
                tracing::error!("Failed to load GLTF file: {}", e);
            }
        }
    }

    #[cfg(not(target_arch = "wasm32"))]
    fn import_fbx(&mut self, world: &mut World) {
        let Some(fbx_path) = rfd::FileDialog::new()
            .add_filter("FBX", &["fbx", "FBX"])
            .pick_file()
        else {
            return;
        };

        self.load_fbx(world, &fbx_path);
    }

    #[cfg(not(target_arch = "wasm32"))]
    fn load_fbx(&mut self, world: &mut World, path: &std::path::Path) {
        tracing::info!("Loading FBX from: {:?}", path);

        let source_path = path
            .canonicalize()
            .ok()
            .and_then(|absolute| absolute.to_str().map(|s| s.to_string()))
            .unwrap_or_else(|| path.display().to_string());

        tracing::info!("FBX source path: {}", source_path);

        match nightshade::ecs::prefab::import_fbx_from_path(path) {
            Ok(result) => {
                tracing::info!("Successfully loaded FBX file");
                tracing::info!("  Meshes: {}", result.meshes.len());
                tracing::info!("  Materials: {}", result.materials.len());
                tracing::info!("  Textures: {}", result.textures.len());
                tracing::info!("  Prefabs: {}", result.prefabs.len());
                tracing::info!("  Animations: {}", result.animations.len());
                tracing::info!("  Skins: {}", result.skins.len());
                tracing::info!("  Node count: {}", result.node_count);

                for (name, mesh) in &result.meshes {
                    tracing::info!(
                        "  Mesh '{}': {} vertices, {} indices, skinned: {}",
                        name,
                        mesh.vertices.len(),
                        mesh.indices.len(),
                        mesh.skin_data.is_some()
                    );
                    if let Some(ref skin_data) = mesh.skin_data {
                        tracing::info!(
                            "    Skinned vertices: {}",
                            skin_data.skinned_vertices.len()
                        );
                    }
                }

                for prefab in &result.prefabs {
                    tracing::info!(
                        "  Prefab '{}': {} root nodes",
                        prefab.name,
                        prefab.root_nodes.len()
                    );
                    for node in &prefab.root_nodes {
                        let has_mesh = node.components.render_mesh.is_some();
                        let name = node
                            .components
                            .name
                            .as_ref()
                            .map(|n| n.0.as_str())
                            .unwrap_or("<unnamed>");
                        tracing::info!(
                            "    Node '{}': has_mesh={}, children={}",
                            name,
                            has_mesh,
                            node.children.len()
                        );
                    }
                }

                for (name, (rgba_data, width, height)) in result.textures {
                    tracing::info!("  Loading texture '{}': {}x{}", name, width, height);
                    world.queue_command(WorldCommand::LoadTexture {
                        name,
                        rgba_data,
                        width,
                        height,
                    });
                }

                let has_meshes = !result.meshes.is_empty();

                for (name, mesh) in result.meshes {
                    mesh_cache_insert(&mut world.resources.mesh_cache, name, mesh);
                }

                let fbx_namespace = path.file_stem().and_then(|s| s.to_str()).unwrap_or("fbx");

                for clip in &result.animations {
                    let clip_name = format!("{}::{}", fbx_namespace, clip.name);
                    tracing::info!("Caching animation: {}", clip_name);
                    nightshade::ecs::prefab::resources::animation_cache_insert(
                        &mut world.resources.animation_cache,
                        clip_name,
                        clip.clone(),
                    );
                }

                if has_meshes {
                    for prefab in result.prefabs {
                        let prefab_name = format!("{}::{}", fbx_namespace, prefab.name);
                        tracing::info!("Caching prefab: {}", prefab_name);
                        let cached = nightshade::ecs::prefab::resources::CachedPrefab {
                            prefab,
                            animations: result.animations.clone(),
                            skins: result.skins.clone(),
                            source_path: Some(source_path.clone()),
                        };
                        nightshade::ecs::prefab::resources::prefab_cache_insert(
                            &mut world.resources.prefab_cache,
                            prefab_name,
                            cached,
                        );
                    }
                }

                self.context.show_animations_window = true;

                self.toast_service.show_toast(
                    format!(
                        "Imported {} (View > Animations to instantiate)",
                        path.display()
                    ),
                    ToastLevel::Success,
                );
            }
            Err(error) => {
                tracing::error!("Failed to load FBX: {}", error);
                self.toast_service
                    .show_toast(format!("Failed to load FBX: {}", error), ToastLevel::Error);
            }
        }
    }

    #[cfg(not(target_arch = "wasm32"))]
    fn import_animation(&mut self, world: &mut World) {
        let Some(selected_entity) = self.context.selected_entity else {
            self.toast_service
                .show_toast("No entity selected".to_string(), ToastLevel::Error);
            return;
        };

        if !world.entity_has_components(selected_entity, nightshade::ecs::world::ANIMATION_PLAYER) {
            self.toast_service.show_toast(
                "Selected entity does not have an AnimationPlayer".to_string(),
                ToastLevel::Error,
            );
            return;
        }

        let Some(fbx_path) = rfd::FileDialog::new()
            .add_filter("FBX", &["fbx", "FBX"])
            .pick_file()
        else {
            return;
        };

        match nightshade::ecs::prefab::import_fbx_from_path(&fbx_path) {
            Ok(load_result) => {
                if load_result.animations.is_empty() {
                    self.toast_service.show_toast(
                        "No animations found in FBX file".to_string(),
                        ToastLevel::Warning,
                    );
                    return;
                }

                if let Some(animation_player) = world.get_animation_player_mut(selected_entity) {
                    let count = load_result.animations.len();
                    animation_player.add_clips(load_result.animations);
                    self.toast_service.show_toast(
                        format!("Added {} animation(s) from {}", count, fbx_path.display()),
                        ToastLevel::Success,
                    );
                }
            }
            Err(error) => {
                self.toast_service
                    .show_toast(format!("Failed to load FBX: {}", error), ToastLevel::Error);
            }
        }
    }

    fn load_gltf_from_bytes(&mut self, world: &mut World, name: &str, data: &[u8]) {
        tracing::info!("Loading GLTF from bytes: {} ({} bytes)", name, data.len());

        let source_path = name.to_string();

        match nightshade::ecs::prefab::import_gltf_from_bytes(data) {
            Ok(result) => {
                tracing::info!("Successfully loaded GLTF from bytes");
                tracing::info!("Loaded {} meshes", result.meshes.len());
                tracing::info!("Loaded {} materials", result.materials.len());
                tracing::info!("Loaded {} textures", result.textures.len());
                tracing::info!("Loaded {} prefabs", result.prefabs.len());
                tracing::info!("Loaded {} animations", result.animations.len());

                for (texture_name, (rgba_data, width, height)) in result.textures {
                    tracing::info!("Loading texture '{}': {}x{}", texture_name, width, height);
                    world.queue_command(WorldCommand::LoadTexture {
                        name: texture_name,
                        rgba_data,
                        width,
                        height,
                    });
                }

                for (mesh_name, mesh) in result.meshes {
                    tracing::info!(
                        "Mesh '{}': {} vertices, {} indices",
                        mesh_name,
                        mesh.vertices.len(),
                        mesh.indices.len()
                    );
                    mesh_cache_insert(&mut world.resources.mesh_cache, mesh_name, mesh);
                }

                let mut last_spawned_entity = None;
                for prefab in &result.prefabs {
                    tracing::info!("Spawning prefab '{}'", prefab.name);
                    let entity = nightshade::ecs::prefab::spawn_prefab_with_source(
                        world,
                        prefab,
                        &result.animations,
                        &result.skins,
                        nalgebra_glm::vec3(0.0, 0.0, 0.0),
                        Some(&source_path),
                    );
                    tracing::info!("Spawned prefab with root entity {:?}", entity);
                    last_spawned_entity = Some(entity);
                }

                if let Some(entity) = last_spawned_entity {
                    self.context.selected_entity = Some(entity);
                }

                for prefab in result.prefabs {
                    self.context.tile_behavior.prefabs.push(PrefabMetadata {
                        prefab,
                        animations: result.animations.clone(),
                        source_path: Some(source_path.clone()),
                    });
                }
            }
            Err(e) => {
                tracing::error!("Failed to load GLTF from bytes: {}", e);
            }
        }
    }

    fn drop_indicator_ui(&self, ui_context: &egui::Context) {
        egui::Area::new(egui::Id::new("drop_indicator"))
            .anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0])
            .show(ui_context, |ui| {
                ui.with_layout(egui::Layout::top_down(egui::Align::Center), |ui| {
                    let frame = egui::Frame::default()
                        .fill(egui::Color32::from_rgba_premultiplied(30, 30, 30, 200))
                        .corner_radius(10.0)
                        .stroke(egui::Stroke::new(
                            2.0,
                            egui::Color32::from_rgb(100, 150, 250),
                        ))
                        .inner_margin(30.0);

                    frame.show(ui, |ui| {
                        if let Some(file_type) = &self.context.drag_file_type {
                            if file_type == "hdr" {
                                ui.heading("Drop HDR file to use as skybox");
                            } else if file_type == "gltf" {
                                ui.heading("Drop GLTF/GLB to import to scene");
                            } else if file_type == "fbx" {
                                ui.heading("Drop FBX to import to scene");
                            } else if file_type == "project" {
                                ui.heading("Drop project file to load");
                            } else {
                                ui.heading("Drop file to load");
                            }
                        } else {
                            ui.heading("Drop file to load");
                        }
                    });
                });
            });
    }

    fn set_gizmo_visible(
        &self,
        world: &mut World,
        gizmo: &nightshade::ecs::gizmos::GizmoState,
        visible: bool,
    ) {
        if let Some(vis) = world.get_visibility_mut(gizmo.root) {
            vis.visible = visible;
        }

        let descendants: Vec<Entity> =
            nightshade::ecs::transform::queries::query_descendants(world, gizmo.root)
                .into_iter()
                .collect();

        for descendant in descendants {
            if let Some(vis) = world.get_visibility_mut(descendant) {
                vis.visible = visible;
            }
        }
    }

    fn update_picking(&mut self, world: &mut World) {
        use nightshade::ecs::gizmos;

        if !self.is_mouse_in_selected_viewport(world) {
            return;
        }

        let is_dragging = !matches!(
            self.context.drag_mode,
            nightshade::ecs::gizmos::GizmoDragMode::None
        );
        if is_dragging {
            return;
        }

        let mouse = &world.resources.input.mouse;
        let mouse_pos = mouse.position;

        if mouse.state.contains(MouseState::LEFT_JUST_PRESSED)
            && !world.resources.user_interface.hud_wants_pointer
            && self.context.hover_axis.is_none()
        {
            let pick_pos = if let Some(viewport_rect) = &world.resources.window.active_viewport_rect
                && let Some((window_width, window_height)) =
                    world.resources.window.cached_viewport_size
            {
                let local = viewport_rect.to_local(mouse_pos);
                let scale_x = window_width as f32 / viewport_rect.width;
                let scale_y = window_height as f32 / viewport_rect.height;
                ((local.x * scale_x) as u32, (local.y * scale_y) as u32)
            } else {
                (mouse_pos.x as u32, mouse_pos.y as u32)
            };
            world
                .resources
                .gpu_picking
                .request_pick(pick_pos.0, pick_pos.1);
        }

        if let Some(result) = world.resources.gpu_picking.take_result() {
            if let Some(entity_id) = result.entity_id {
                let camera_entity = world.resources.active_camera;
                let gizmo = self.context.gizmo.as_ref();

                let found_entity = world
                    .query_entities(
                        nightshade::ecs::RENDER_MESH | nightshade::ecs::GLOBAL_TRANSFORM,
                    )
                    .find(|entity| {
                        if entity.id != entity_id {
                            return false;
                        }
                        if Some(*entity) == camera_entity {
                            return false;
                        }
                        if let Some(gizmo) = gizmo
                            && gizmos::is_gizmo_entity(world, gizmo, *entity)
                        {
                            return false;
                        }
                        true
                    });

                self.context.selected_entity = found_entity;
                world.resources.graphics.bounding_volume_selected_entity = found_entity;
            } else {
                self.context.selected_entity = None;
                world.resources.graphics.bounding_volume_selected_entity = None;
            }
        }
    }

    fn check_context_menu_trigger(&mut self, world: &World) {
        if !self.is_mouse_in_selected_viewport(world) {
            return;
        }

        if self.context.selected_entity.is_none() {
            return;
        }

        let in_modal_transform =
            self.context.modal_transform.operation != views::TransformOperation::None;
        if in_modal_transform {
            return;
        }

        let is_dragging = !matches!(
            self.context.drag_mode,
            nightshade::ecs::gizmos::GizmoDragMode::None
        );
        if is_dragging {
            return;
        }

        let mouse = &world.resources.input.mouse;
        if mouse
            .state
            .contains(nightshade::prelude::MouseState::RIGHT_JUST_PRESSED)
        {
            self.context.entity_context_menu = Some(mouse.position);
        }
    }

    fn clear_scene(&mut self, world: &mut World) {
        use nightshade::ecs::gizmos;

        tracing::info!("Clearing scene");

        if let Some(old_gizmo) = self.context.gizmo.take() {
            gizmos::destroy_gizmo(world, &old_gizmo);
        }

        let camera_entity = world.resources.active_camera;
        let all_entities: Vec<Entity> = world.get_all_entities();

        for entity in all_entities {
            if Some(entity) == camera_entity {
                continue;
            }

            world.queue_command(WorldCommand::DespawnRecursive { entity });
        }

        self.context.selected_entity = None;
        self.context.hover_axis = None;
        self.context.drag_mode = nightshade::ecs::gizmos::GizmoDragMode::None;

        world.resources.graphics.atmosphere = Atmosphere::Sky;
        spawn_sun(world);

        tracing::info!("Scene cleared");
    }

    fn new_project(&mut self, world: &mut World) {
        self.clear_scene(world);
        self.context.current_project_path = None;
        self.context.current_project_name = None;
        self.context.selected_entity = None;
        self.context.hdr_skybox_path = None;
        self.context.editing_project_name = false;
        self.context.project_name_edit_buffer.clear();
        self.context.editing_layout_index = None;
        self.context.layout_name_edit_buffer.clear();
        self.context.layout_is_modified = false;

        let default_tree = Self::create_default_tile_tree();
        self.context.tile_tree = Some(default_tree.clone());

        #[cfg(not(target_arch = "wasm32"))]
        {
            let mut project = project::EditorProject::new(None);
            project.add_map("Untitled".to_string(), self.world_to_map(world));
            project.add_layout("Default".to_string(), default_tree);
            self.project = Some(project);
            self.project_state = project::ProjectState::default();
            self.toast_service
                .show_toast("Created new project", ToastLevel::Success);
        }
    }

    fn create_default_tile_tree() -> nightshade::prelude::egui_tiles::Tree<Pane> {
        let mut tiles = nightshade::prelude::egui_tiles::Tiles::default();
        let viewport = tiles.insert_pane(Pane::camera(0));
        nightshade::prelude::egui_tiles::Tree::new("tile_tree", viewport, tiles)
    }

    fn update_project_from_current_state(&mut self, world: &World) {
        let map = self.world_to_map(world);
        let tree = self.context.tile_tree.clone();
        if let Some(ref mut project) = self.project {
            if let Some(active_map_name) = &project.active_map_name.clone() {
                project.maps.insert(active_map_name.clone(), map);
            }
            if let Some(layout) = project.active_layout_mut()
                && let Some(tree) = tree
            {
                layout.tree = tree;
            }
        }
    }

    #[cfg(not(target_arch = "wasm32"))]
    fn save_project(&mut self, world: &World) {
        if let Some(path) = &self.context.current_project_path.clone() {
            self.update_project_from_current_state(world);
            if let Some(ref project) = self.project {
                match project.save(path) {
                    Ok(()) => {
                        let path_str = path.display().to_string();
                        self.project_state.is_modified = false;
                        self.context.layout_is_modified = false;
                        self.toast_service
                            .show_toast(format!("Saved to {}", path_str), ToastLevel::Success);
                    }
                    Err(error) => {
                        tracing::error!("Failed to save project: {}", error);
                        self.toast_service
                            .show_toast(format!("Failed to save: {}", error), ToastLevel::Error);
                    }
                }
            }
        } else {
            self.save_project_as(world);
        }
    }

    #[cfg(not(target_arch = "wasm32"))]
    fn save_project_as(&mut self, world: &World) {
        if let Some(path) = rfd::FileDialog::new()
            .add_filter("JSON Project", &["project.json"])
            .add_filter("Binary Project", &["project.bin"])
            .set_file_name("project.project.json")
            .save_file()
        {
            if self.project.is_none() {
                let mut new_project =
                    project::EditorProject::new(self.context.current_project_name.clone());
                new_project.add_map("Untitled".to_string(), self.world_to_map(world));
                if let Some(tree) = self.context.tile_tree.clone() {
                    new_project.add_layout("Default".to_string(), tree);
                }
                self.project = Some(new_project);
            }
            self.update_project_from_current_state(world);
            if let Some(ref mut project) = self.project {
                project.project_name = self.context.current_project_name.clone();
                match project.save(&path) {
                    Ok(()) => {
                        let path_str = path.display().to_string();
                        self.context.current_project_path = Some(path.clone());
                        self.project_state.is_open = true;
                        self.project_state.is_modified = false;
                        self.context.layout_is_modified = false;
                        self.settings.add_recent_project(path_str.clone());
                        if let Err(error) = self.settings.save() {
                            tracing::error!("Failed to save settings: {error}");
                        }
                        self.toast_service
                            .show_toast(format!("Saved to {}", path_str), ToastLevel::Success);
                    }
                    Err(error) => {
                        tracing::error!("Failed to save project: {}", error);
                        self.toast_service
                            .show_toast(format!("Failed to save: {}", error), ToastLevel::Error);
                    }
                }
            }
        }
    }

    #[cfg(not(target_arch = "wasm32"))]
    fn collect_all_and_save(&mut self, world: &World) {
        let Some(save_dir) = rfd::FileDialog::new()
            .set_title("Select directory to save project")
            .pick_folder()
        else {
            return;
        };

        if self.project.is_none() {
            let mut new_project =
                project::EditorProject::new(self.context.current_project_name.clone());
            new_project.add_map("Untitled".to_string(), self.world_to_map(world));
            if let Some(tree) = self.context.tile_tree.clone() {
                new_project.add_layout("Default".to_string(), tree);
            }
            self.project = Some(new_project);
        }
        self.update_project_from_current_state(world);

        let Some(ref mut project) = self.project else {
            return;
        };

        let assets_dir = save_dir.join("assets");
        if let Err(error) = std::fs::create_dir_all(&assets_dir) {
            tracing::error!("Failed to create assets directory: {}", error);
            self.toast_service.show_toast(
                format!("Failed to create assets directory: {}", error),
                ToastLevel::Error,
            );
            return;
        }

        let mut copied_files: std::collections::HashMap<String, String> =
            std::collections::HashMap::new();
        let mut copy_errors: Vec<String> = Vec::new();

        for map in project.maps.values_mut() {
            if let Some(nightshade::ecs::map::MapHdrSkybox::Reference { path }) =
                &mut map.hdr_skybox
            {
                let source_path = std::path::Path::new(&*path);
                if source_path.exists() {
                    let file_name = source_path
                        .file_name()
                        .and_then(|name| name.to_str())
                        .unwrap_or("skybox.hdr");

                    if let Some(new_relative_path) = copied_files.get(&*path) {
                        *path = new_relative_path.clone();
                    } else {
                        let dest_path = assets_dir.join(file_name);
                        let relative_path = format!("assets/{}", file_name);

                        if !dest_path.exists() {
                            match std::fs::copy(source_path, &dest_path) {
                                Ok(_) => {
                                    tracing::info!(
                                        "Copied HDR {} to {}",
                                        source_path.display(),
                                        dest_path.display()
                                    );
                                }
                                Err(error) => {
                                    copy_errors
                                        .push(format!("Failed to copy HDR '{}': {}", path, error));
                                }
                            }
                        }

                        if dest_path.exists() {
                            copied_files.insert(path.clone(), relative_path.clone());
                            *path = relative_path;
                        }
                    }
                } else {
                    copy_errors.push(format!("HDR skybox file not found: {}", path));
                }
            }

            for (prefab_name, map_prefab) in map.prefabs.iter_mut() {
                if let nightshade::ecs::map::MapPrefab::Reference { path } = map_prefab {
                    let source_path = std::path::Path::new(&*path);
                    if !source_path.exists() {
                        copy_errors.push(format!("Source file not found: {}", path));
                        continue;
                    }

                    let file_name = source_path
                        .file_name()
                        .and_then(|name| name.to_str())
                        .unwrap_or("unknown.glb");

                    if let Some(new_relative_path) = copied_files.get(&*path) {
                        *path = new_relative_path.clone();
                        continue;
                    }

                    let dest_path = assets_dir.join(file_name);
                    let relative_path = format!("assets/{}", file_name);

                    if !dest_path.exists() {
                        match std::fs::copy(source_path, &dest_path) {
                            Ok(_) => {
                                tracing::info!(
                                    "Copied {} to {}",
                                    source_path.display(),
                                    dest_path.display()
                                );
                            }
                            Err(error) => {
                                copy_errors.push(format!(
                                    "Failed to copy '{}' for prefab '{}': {}",
                                    path, prefab_name, error
                                ));
                                continue;
                            }
                        }
                    }

                    copied_files.insert(path.clone(), relative_path.clone());
                    *path = relative_path;
                }
            }
        }

        for error in &copy_errors {
            tracing::warn!("{}", error);
            self.toast_service
                .show_toast(error.clone(), ToastLevel::Warning);
        }

        let project_name = project
            .project_name
            .clone()
            .unwrap_or_else(|| "project".to_string());
        let project_file = save_dir.join(format!("{}.project.json", project_name));

        project.project_name = self.context.current_project_name.clone();
        match project.save(&project_file) {
            Ok(()) => {
                let path_str = project_file.display().to_string();
                self.context.current_project_path = Some(project_file.clone());
                self.project_state.is_open = true;
                self.project_state.is_modified = false;
                self.context.layout_is_modified = false;
                self.settings.add_recent_project(path_str.clone());
                if let Err(error) = self.settings.save() {
                    tracing::error!("Failed to save settings: {error}");
                }
                let copied_count = copied_files.len();
                self.toast_service.show_toast(
                    format!(
                        "Saved to {} with {} assets collected",
                        path_str, copied_count
                    ),
                    ToastLevel::Success,
                );
            }
            Err(error) => {
                tracing::error!("Failed to save project: {}", error);
                self.toast_service
                    .show_toast(format!("Failed to save: {}", error), ToastLevel::Error);
            }
        }
    }

    #[cfg(not(target_arch = "wasm32"))]
    fn load_project(&mut self, world: &mut World) {
        if let Some(path) = rfd::FileDialog::new()
            .add_filter("JSON Project", &["project.json"])
            .add_filter("Binary Project", &["project.bin"])
            .pick_file()
        {
            self.load_project_from_path(world, &path);
        }
    }

    #[cfg(not(target_arch = "wasm32"))]
    fn load_project_from_path(&mut self, world: &mut World, path: &std::path::Path) {
        match project::EditorProject::load(path) {
            Ok(mut project) => {
                let project_dir = path.parent();
                Self::resolve_project_relative_paths(&mut project, project_dir);

                self.clear_scene(world);

                self.context.hdr_skybox_path = None;
                if let Some(active_map) = project.active_map() {
                    if let Some(nightshade::ecs::map::MapHdrSkybox::Reference { path }) =
                        &active_map.hdr_skybox
                    {
                        self.context.hdr_skybox_path = Some(std::path::PathBuf::from(path));
                    }

                    match nightshade::ecs::map::spawn_map(world, active_map) {
                        Ok(result) => {
                            for warning in result.warnings {
                                tracing::warn!("{}", warning);
                                self.toast_service.show_toast(warning, ToastLevel::Warning);
                            }
                        }
                        Err(error) => {
                            tracing::error!("Failed to spawn map: {}", error);
                            self.toast_service.show_toast(
                                format!("Failed to spawn: {}", error),
                                ToastLevel::Error,
                            );
                            return;
                        }
                    }

                    self.cleanup_gizmo_entities(world);
                }

                if project.layouts.is_empty() {
                    let default_tree = Self::create_default_tile_tree();
                    project.add_layout("Default".to_string(), default_tree.clone());
                    self.context.tile_tree = Some(default_tree);
                } else if let Some(layout) = project.active_layout() {
                    self.context.tile_tree = Some(layout.tree.clone());
                }

                let path_str = path.display().to_string();
                self.context.current_project_path = Some(path.to_path_buf());
                self.context.current_project_name = project.project_name.clone();
                self.context.selected_entity = None;
                self.context.editing_project_name = false;
                self.context.project_name_edit_buffer.clear();
                self.context.editing_layout_index = None;
                self.context.layout_name_edit_buffer.clear();
                self.context.layout_is_modified = false;
                self.project = Some(project);
                self.project_state.is_open = true;
                self.project_state.is_modified = false;
                self.settings.add_recent_project(path_str.clone());
                if let Err(error) = self.settings.save() {
                    tracing::error!("Failed to save settings: {error}");
                }
                self.toast_service
                    .show_toast(format!("Loaded from {}", path_str), ToastLevel::Success);
            }
            Err(error) => {
                tracing::error!("Failed to load project: {}", error);
                self.toast_service
                    .show_toast(format!("Failed to load: {}", error), ToastLevel::Error);
            }
        }
    }

    #[cfg(not(target_arch = "wasm32"))]
    fn load_map_file(&mut self, world: &mut World) {
        if let Some(path) = rfd::FileDialog::new()
            .add_filter("JSON Map", &["json"])
            .add_filter("Binary Map", &["bin"])
            .pick_file()
        {
            self.load_map_from_path(world, &path);
        }
    }

    #[cfg(not(target_arch = "wasm32"))]
    fn load_map_from_path(&mut self, world: &mut World, path: &std::path::Path) {
        match nightshade::ecs::map::load_map(path) {
            Ok(map) => {
                let map_name = map.name.clone();

                if self.project.is_none() {
                    self.new_project(world);
                }

                self.update_project_from_current_state(world);
                self.clear_scene(world);

                self.context.hdr_skybox_path = None;
                if let Some(nightshade::ecs::map::MapHdrSkybox::Reference { path: ref hdr_path }) =
                    map.hdr_skybox
                {
                    self.context.hdr_skybox_path = Some(std::path::PathBuf::from(hdr_path));
                }

                match nightshade::ecs::map::spawn_map(world, &map) {
                    Ok(result) => {
                        for warning in result.warnings {
                            tracing::warn!("{}", warning);
                            self.toast_service.show_toast(warning, ToastLevel::Warning);
                        }
                        self.cleanup_gizmo_entities(world);

                        if let Some(ref mut project) = self.project {
                            project.add_map(map_name.clone(), map);
                            project.set_active_map(&map_name);
                        }
                        self.project_state.is_modified = true;

                        self.toast_service
                            .show_toast(format!("Loaded map: {}", map_name), ToastLevel::Success);
                    }
                    Err(error) => {
                        tracing::error!("Failed to spawn map: {}", error);
                        self.toast_service
                            .show_toast(format!("Failed to spawn: {}", error), ToastLevel::Error);
                    }
                }
            }
            Err(error) => {
                tracing::error!("Failed to load map: {}", error);
                self.toast_service
                    .show_toast(format!("Failed to load map: {}", error), ToastLevel::Error);
            }
        }
    }

    fn load_map_from_bytes(&mut self, world: &mut World, name: &str, data: &[u8]) {
        let map: nightshade::ecs::map::Map = match serde_json::from_slice(data) {
            Ok(map) => map,
            Err(_json_error) => {
                #[cfg(not(target_arch = "wasm32"))]
                {
                    match bincode::deserialize(data) {
                        Ok(map) => map,
                        Err(error) => {
                            tracing::error!("Failed to parse map data: {}", error);
                            self.toast_service.show_toast(
                                format!("Failed to parse map: {}", error),
                                ToastLevel::Error,
                            );
                            return;
                        }
                    }
                }
                #[cfg(target_arch = "wasm32")]
                {
                    tracing::error!("Failed to parse map data: {}", _json_error);
                    return;
                }
            }
        };

        let map_name = map.name.clone();

        if self.project.is_none() {
            self.new_project(world);
        }

        self.update_project_from_current_state(world);
        self.clear_scene(world);

        #[cfg(not(target_arch = "wasm32"))]
        {
            self.context.hdr_skybox_path = None;
            if let Some(nightshade::ecs::map::MapHdrSkybox::Reference { path: ref hdr_path }) =
                map.hdr_skybox
            {
                self.context.hdr_skybox_path = Some(std::path::PathBuf::from(hdr_path));
            }
        }

        match nightshade::ecs::map::spawn_map(world, &map) {
            Ok(result) => {
                for warning in result.warnings {
                    tracing::warn!("{}", warning);
                    #[cfg(not(target_arch = "wasm32"))]
                    self.toast_service.show_toast(warning, ToastLevel::Warning);
                }
                self.cleanup_gizmo_entities(world);

                if let Some(ref mut project) = self.project {
                    project.add_map(map_name.clone(), map);
                    project.set_active_map(&map_name);
                }
                self.project_state.is_modified = true;

                tracing::info!("Loaded map from dropped data: {}", name);
                #[cfg(not(target_arch = "wasm32"))]
                self.toast_service
                    .show_toast(format!("Loaded map: {}", map_name), ToastLevel::Success);
            }
            Err(error) => {
                tracing::error!("Failed to spawn map: {}", error);
                #[cfg(not(target_arch = "wasm32"))]
                self.toast_service
                    .show_toast(format!("Failed to spawn: {}", error), ToastLevel::Error);
            }
        }
    }

    #[cfg(not(target_arch = "wasm32"))]
    fn resolve_project_relative_paths(
        project: &mut project::EditorProject,
        project_dir: Option<&std::path::Path>,
    ) {
        let Some(base_dir) = project_dir else {
            return;
        };

        for map in project.maps.values_mut() {
            if let Some(nightshade::ecs::map::MapHdrSkybox::Reference { path }) =
                &mut map.hdr_skybox
            {
                let path_buf = std::path::Path::new(&*path);
                if path_buf.is_relative() {
                    let absolute = base_dir.join(path_buf);
                    if let Some(absolute_str) = absolute.to_str() {
                        *path = absolute_str.to_string();
                    }
                }
            }

            for map_prefab in map.prefabs.values_mut() {
                if let nightshade::ecs::map::MapPrefab::Reference { path } = map_prefab {
                    let path_buf = std::path::Path::new(&*path);
                    if path_buf.is_relative() {
                        let absolute = base_dir.join(path_buf);
                        if let Some(absolute_str) = absolute.to_str() {
                            *path = absolute_str.to_string();
                        }
                    }
                }
            }
        }
    }

    fn switch_to_map(&mut self, world: &mut World, map_name: &str) {
        self.update_project_from_current_state(world);
        let map_to_spawn = if let Some(ref mut project) = self.project {
            if project.set_active_map(map_name) {
                project.active_map().cloned()
            } else {
                None
            }
        } else {
            None
        };
        if let Some(map) = map_to_spawn {
            self.context.hdr_skybox_path = None;
            #[cfg(not(target_arch = "wasm32"))]
            if let Some(nightshade::ecs::map::MapHdrSkybox::Reference { path }) = &map.hdr_skybox {
                self.context.hdr_skybox_path = Some(std::path::PathBuf::from(path));
            }

            self.clear_scene(world);
            match nightshade::ecs::map::spawn_map(world, &map) {
                Ok(result) => {
                    for warning in result.warnings {
                        tracing::warn!("{}", warning);
                        #[cfg(not(target_arch = "wasm32"))]
                        self.toast_service.show_toast(warning, ToastLevel::Warning);
                    }
                    self.cleanup_gizmo_entities(world);
                    #[cfg(not(target_arch = "wasm32"))]
                    self.toast_service.show_toast(
                        format!("Switched to map: {}", map_name),
                        ToastLevel::Success,
                    );
                    #[cfg(target_arch = "wasm32")]
                    tracing::info!("Switched to map: {}", map_name);
                }
                Err(error) => {
                    tracing::error!("Failed to spawn map: {}", error);
                    #[cfg(not(target_arch = "wasm32"))]
                    self.toast_service
                        .show_toast(format!("Failed to spawn: {}", error), ToastLevel::Error);
                }
            }
        }
    }

    fn create_new_map(&mut self, world: &mut World, map_name: String) {
        self.update_project_from_current_state(world);
        self.clear_scene(world);
        world.resources.graphics.atmosphere = Atmosphere::Sky;
        spawn_sun(world);
        let map = self.world_to_map(world);
        if let Some(ref mut project) = self.project {
            project.add_map(map_name.clone(), map);
            project.set_active_map(&map_name);
        }
        self.project_state.is_modified = true;
        #[cfg(not(target_arch = "wasm32"))]
        self.toast_service
            .show_toast(format!("Created map: {}", map_name), ToastLevel::Success);
        #[cfg(target_arch = "wasm32")]
        tracing::info!("Created map: {}", map_name);
    }

    fn delete_current_map(&mut self, world: &mut World) {
        let can_delete = self
            .project
            .as_ref()
            .map(|p| p.maps.len() > 1)
            .unwrap_or(false);
        if !can_delete {
            #[cfg(not(target_arch = "wasm32"))]
            self.toast_service
                .show_toast("Cannot delete the last map", ToastLevel::Warning);
            #[cfg(target_arch = "wasm32")]
            tracing::warn!("Cannot delete the last map");
            return;
        }
        let (deleted_name, map_to_spawn) = if let Some(ref mut project) = self.project {
            if let Some(map_name) = project.active_map_name.clone() {
                project.remove_map(&map_name);
                (Some(map_name), project.active_map().cloned())
            } else {
                (None, None)
            }
        } else {
            (None, None)
        };
        if let Some(map_name) = deleted_name {
            self.project_state.is_modified = true;
            #[cfg(not(target_arch = "wasm32"))]
            {
                self.context.hdr_skybox_path = None;
            }
            self.clear_scene(world);
            if let Some(map) = map_to_spawn {
                #[cfg(not(target_arch = "wasm32"))]
                if let Some(nightshade::ecs::map::MapHdrSkybox::Reference { path }) =
                    &map.hdr_skybox
                {
                    self.context.hdr_skybox_path = Some(std::path::PathBuf::from(path));
                }
                if let Ok(result) = nightshade::ecs::map::spawn_map(world, &map) {
                    for warning in result.warnings {
                        tracing::warn!("{}", warning);
                        #[cfg(not(target_arch = "wasm32"))]
                        self.toast_service.show_toast(warning, ToastLevel::Warning);
                    }
                    self.cleanup_gizmo_entities(world);
                }
            }
            #[cfg(not(target_arch = "wasm32"))]
            self.toast_service
                .show_toast(format!("Deleted map: {}", map_name), ToastLevel::Success);
            #[cfg(target_arch = "wasm32")]
            tracing::info!("Deleted map: {}", map_name);
        }
    }

    #[cfg(not(target_arch = "wasm32"))]
    fn set_as_startup_project(&mut self) {
        if let Some(path) = &self.context.current_project_path {
            let path_str = path
                .canonicalize()
                .ok()
                .and_then(|canonical| canonical.to_str().map(|str| str.to_string()))
                .unwrap_or_else(|| path.display().to_string());
            self.settings.startup_project = Some(path_str.clone());
            if let Err(error) = self.settings.save() {
                tracing::error!("Failed to save settings: {error}");
            }
            self.toast_service
                .show_toast("Set as startup project", ToastLevel::Success);
        }
    }

    #[cfg(not(target_arch = "wasm32"))]
    fn unset_startup_project(&mut self) {
        self.settings.startup_project = None;
        if let Err(error) = self.settings.save() {
            tracing::error!("Failed to save settings: {error}");
        }
        self.toast_service
            .show_toast("Unset startup project", ToastLevel::Success);
    }

    #[cfg(not(target_arch = "wasm32"))]
    fn switch_to_layout(&mut self, index: usize) {
        if let Some(ref mut project) = self.project {
            if let Some(current_tree) = self.context.tile_tree.take()
                && let Some(current_layout) = project.layouts.get_mut(project.active_layout_index)
            {
                current_layout.tree = current_tree;
            }

            if self.context.layout_is_modified {
                self.project_state.is_modified = true;
            }

            if index < project.layouts.len() {
                project.active_layout_index = index;
                self.context.layout_is_modified = false;
                if let Some(layout) = project.layouts.get(index) {
                    self.context.tile_tree = Some(layout.tree.clone());
                    self.toast_service.show_toast(
                        format!("Switched to layout: {}", layout.name),
                        ToastLevel::Success,
                    );
                }
            }
        }
    }

    #[cfg(not(target_arch = "wasm32"))]
    fn create_new_layout(&mut self) {
        let layout_count = self.project.as_ref().map(|p| p.layouts.len()).unwrap_or(0);
        let new_layout_name = format!("Layout {}", layout_count + 1);

        let new_tree = Self::create_default_tile_tree();

        if let Some(ref mut project) = self.project {
            project.add_layout(new_layout_name.clone(), new_tree);
            self.project_state.is_modified = true;
            self.toast_service.show_toast(
                format!("Created layout: {}", new_layout_name),
                ToastLevel::Success,
            );
        }
    }

    #[cfg(not(target_arch = "wasm32"))]
    fn save_current_layout(&mut self) {
        if let Some(ref mut project) = self.project
            && let Some(current_tree) = self.context.tile_tree.clone()
            && let Some(current_layout) = project.layouts.get_mut(project.active_layout_index)
        {
            current_layout.tree = current_tree;
            self.project_state.is_modified = true;
            self.context.layout_is_modified = false;
            self.toast_service.show_toast(
                format!("Saved layout: {}", current_layout.name),
                ToastLevel::Success,
            );
        }
    }

    #[cfg(not(target_arch = "wasm32"))]
    fn delete_current_layout(&mut self) {
        if let Some(ref mut project) = self.project {
            if project.layouts.len() <= 1 {
                self.toast_service
                    .show_toast("Cannot delete the last layout", ToastLevel::Warning);
                return;
            }

            let current_index = project.active_layout_index;
            let deleted_name = project
                .layouts
                .get(current_index)
                .map(|l| l.name.clone())
                .unwrap_or_default();

            project.layouts.remove(current_index);

            if project.active_layout_index >= project.layouts.len() {
                project.active_layout_index = project.layouts.len().saturating_sub(1);
            }

            if let Some(layout) = project.layouts.get(project.active_layout_index) {
                self.context.tile_tree = Some(layout.tree.clone());
            }

            self.context.editing_layout_index = None;
            self.context.layout_name_edit_buffer.clear();
            self.context.layout_is_modified = false;

            self.project_state.is_modified = true;
            self.toast_service.show_toast(
                format!("Deleted layout: {}", deleted_name),
                ToastLevel::Success,
            );
        }
    }

    fn recreate_gizmo_for_mode(&mut self, world: &mut World) {
        use nightshade::ecs::gizmos;

        if let Some(old_gizmo) = self.context.gizmo.take() {
            gizmos::destroy_gizmo(world, &old_gizmo);
        }

        self.context.hover_axis = None;
        self.context.drag_mode = nightshade::ecs::gizmos::GizmoDragMode::None;
    }

    fn start_modal_transform(&mut self, world: &mut World, operation: views::TransformOperation) {
        let Some(entity) = self.context.selected_entity else {
            return;
        };
        let Some(transform) = world.get_local_transform(entity) else {
            return;
        };

        self.context.modal_transform.operation = operation;
        self.context.modal_transform.constraint = views::TransformConstraint::None;
        self.context.modal_transform.initial_transform = *transform;
        self.context.modal_transform.start_mouse_pos = world.resources.input.mouse.position;
        self.context.modal_transform.accumulated_delta = Vec2::zeros();

        self.context
            .undo_history
            .begin_transform_change(entity, *transform);
    }

    fn confirm_modal_transform(&mut self, world: &mut World) {
        if let Some(entity) = self.context.selected_entity
            && let Some(transform) = world.get_local_transform(entity)
        {
            self.context
                .undo_history
                .commit_transform_change(entity, *transform);
            self.project_state.is_modified = true;
        }
        self.context.modal_transform = views::ModalTransformState::default();
    }

    fn cancel_modal_transform(&mut self, world: &mut World) {
        if let Some(entity) = self.context.selected_entity {
            world.set_local_transform(entity, self.context.modal_transform.initial_transform);
            mark_local_transform_dirty(world, entity);
        }
        self.context.modal_transform = views::ModalTransformState::default();
    }

    fn update_modal_transform(&mut self, world: &mut World) {
        if self.context.modal_transform.operation == views::TransformOperation::None {
            return;
        }

        let Some(entity) = self.context.selected_entity else {
            self.context.modal_transform = views::ModalTransformState::default();
            return;
        };

        let mouse = &world.resources.input.mouse;
        let mouse_delta = mouse.position - self.context.modal_transform.start_mouse_pos;

        if mouse
            .state
            .contains(nightshade::prelude::MouseState::LEFT_JUST_PRESSED)
        {
            self.confirm_modal_transform(world);
            return;
        }
        if mouse
            .state
            .contains(nightshade::prelude::MouseState::RIGHT_JUST_PRESSED)
        {
            self.cancel_modal_transform(world);
            return;
        }

        let initial = self.context.modal_transform.initial_transform;
        let constraint = self.context.modal_transform.constraint;
        let operation = self.context.modal_transform.operation;

        let Some(camera_entity) = world.resources.active_camera else {
            return;
        };
        let Some(camera_transform) = world.get_global_transform(camera_entity) else {
            return;
        };

        let camera_right = nalgebra_glm::vec3(
            camera_transform.0[(0, 0)],
            camera_transform.0[(1, 0)],
            camera_transform.0[(2, 0)],
        )
        .normalize();
        let camera_up = nalgebra_glm::vec3(
            camera_transform.0[(0, 1)],
            camera_transform.0[(1, 1)],
            camera_transform.0[(2, 1)],
        )
        .normalize();

        match operation {
            views::TransformOperation::Grab => {
                let sensitivity = 0.01 * self.context.camera_distance;
                let world_movement = camera_right * mouse_delta.x * sensitivity
                    - camera_up * mouse_delta.y * sensitivity;

                let constrained_movement = match constraint {
                    views::TransformConstraint::None => world_movement,
                    views::TransformConstraint::X => Vec3::new(world_movement.x, 0.0, 0.0),
                    views::TransformConstraint::Y => Vec3::new(0.0, world_movement.y, 0.0),
                    views::TransformConstraint::Z => Vec3::new(0.0, 0.0, world_movement.z),
                };

                let local_movement =
                    if let Some(parent_entity) = world.get_parent(entity).and_then(|p| p.0) {
                        if let Some(parent_transform) = world.get_global_transform(parent_entity) {
                            rotate_vec3_by_inverse_global_transform(
                                parent_transform,
                                &constrained_movement,
                            )
                        } else {
                            constrained_movement
                        }
                    } else {
                        constrained_movement
                    };

                let mut new_transform = initial;
                new_transform.translation += local_movement;
                world.set_local_transform(entity, new_transform);
                mark_local_transform_dirty(world, entity);
            }
            views::TransformOperation::Rotate => {
                let sensitivity = 0.01;
                let angle = mouse_delta.x * sensitivity;

                let axis = match constraint {
                    views::TransformConstraint::None | views::TransformConstraint::Y => Vec3::y(),
                    views::TransformConstraint::X => Vec3::x(),
                    views::TransformConstraint::Z => Vec3::z(),
                };

                let rotation = nalgebra_glm::quat_angle_axis(angle, &axis);
                let mut new_transform = initial;
                new_transform.rotation = rotation * initial.rotation;
                world.set_local_transform(entity, new_transform);
                mark_local_transform_dirty(world, entity);
            }
            views::TransformOperation::Scale => {
                let sensitivity = 0.01;
                let scale_factor = 1.0 + mouse_delta.x * sensitivity;
                let scale_factor = scale_factor.max(0.01);

                let scale_delta = match constraint {
                    views::TransformConstraint::None => {
                        Vec3::new(scale_factor, scale_factor, scale_factor)
                    }
                    views::TransformConstraint::X => Vec3::new(scale_factor, 1.0, 1.0),
                    views::TransformConstraint::Y => Vec3::new(1.0, scale_factor, 1.0),
                    views::TransformConstraint::Z => Vec3::new(1.0, 1.0, scale_factor),
                };

                let mut new_transform = initial;
                new_transform.scale = Vec3::new(
                    initial.scale.x * scale_delta.x,
                    initial.scale.y * scale_delta.y,
                    initial.scale.z * scale_delta.z,
                );
                world.set_local_transform(entity, new_transform);
                mark_local_transform_dirty(world, entity);
            }
            views::TransformOperation::None => {}
        }
    }

    fn delete_selected_entity(&mut self, world: &mut World) {
        if let Some(gizmo) = &self.context.gizmo {
            world.update_parent(gizmo.root, None);
        }

        if let Some(entity) = self.context.selected_entity.take() {
            let hierarchy = Box::new(views::UndoHistory::capture_hierarchy(world, entity));
            let entity_name = world
                .get_name(entity)
                .map(|n| n.0.clone())
                .unwrap_or_else(|| format!("Entity {}", entity.id));
            self.context.undo_history.push(
                views::UndoableOperation::EntityDeleted {
                    hierarchy,
                    deleted_entity: entity,
                },
                format!("Delete {}", entity_name),
            );
            despawn_recursive_immediate(world, entity);
            self.project_state.is_modified = true;
        }
    }

    fn duplicate_selected_entity(&mut self, world: &mut World) {
        let Some(entity) = self.context.selected_entity else {
            return;
        };

        if let Some(gizmo) = &self.context.gizmo {
            world.update_parent(gizmo.root, None);
        }

        let source_hierarchy = views::UndoHistory::capture_hierarchy(world, entity);
        let parent = world.get_parent(entity).and_then(|p| p.0);

        let new_entity = self.recreate_hierarchy_for_duplicate(world, &source_hierarchy, parent);

        let new_hierarchy = Box::new(views::UndoHistory::capture_hierarchy(world, new_entity));
        self.context.undo_history.push(
            views::UndoableOperation::EntityCreated {
                hierarchy: new_hierarchy,
                current_entity: new_entity,
            },
            "Duplicate entity".to_string(),
        );

        self.context.selected_entity = Some(new_entity);
        self.project_state.is_modified = true;

        self.start_modal_transform(world, views::TransformOperation::Grab);
    }

    fn recreate_hierarchy_for_duplicate(
        &self,
        world: &mut World,
        node: &views::HierarchyNode,
        parent: Option<Entity>,
    ) -> Entity {
        let entity = self.recreate_entity_for_duplicate(world, &node.snapshot, parent);

        for child_node in &node.children {
            self.recreate_hierarchy_for_duplicate(world, child_node, Some(entity));
        }

        entity
    }

    fn recreate_entity_for_duplicate(
        &self,
        world: &mut World,
        snapshot: &views::EntitySnapshot,
        override_parent: Option<Entity>,
    ) -> Entity {
        let entities = EntityBuilder::new()
            .with_local_transform(snapshot.local_transform.unwrap_or_default())
            .with_global_transform(snapshot.global_transform.unwrap_or_default())
            .spawn(world, 1);
        let entity = entities[0];

        if let Some(name) = &snapshot.name {
            world.set_name(entity, name.clone());
        }

        if let Some(parent_entity) = override_parent {
            world.set_parent(entity, Parent(Some(parent_entity)));
        }

        if let Some(camera) = &snapshot.camera {
            world.set_camera(entity, *camera);
        }
        if let Some(light) = &snapshot.light {
            world.set_light(entity, light.clone());
        }
        if let Some(visibility) = &snapshot.visibility {
            world.set_visibility(entity, visibility.clone());
        }
        if let Some(render_mesh) = &snapshot.render_mesh {
            world.set_render_mesh(entity, render_mesh.clone());
            if let Some(&index) = world
                .resources
                .mesh_cache
                .registry
                .name_to_index
                .get(&render_mesh.name)
            {
                world.resources.mesh_cache.registry.add_reference(index);
            }
        }
        if let Some(material_ref) = &snapshot.material_ref {
            world.set_material_ref(entity, material_ref.clone());
            if let Some(&index) = world
                .resources
                .material_registry
                .registry
                .name_to_index
                .get(&material_ref.name)
            {
                world
                    .resources
                    .material_registry
                    .registry
                    .add_reference(index);
            }
        }
        if let Some(render_layer) = &snapshot.render_layer {
            world.set_render_layer(entity, *render_layer);
        }
        if let Some(script) = &snapshot.script {
            world.set_script(entity, script.clone());
        }
        if snapshot.casts_shadow {
            world.add_components(entity, CASTS_SHADOW);
        }

        world.resources.children_cache_valid = false;
        entity
    }

    fn set_camera_view(&mut self, world: &mut World, view: CameraView) {
        let Some(camera_entity) = world.resources.active_camera else {
            return;
        };

        let Some(pan_orbit) = world.get_pan_orbit_camera_mut(camera_entity) else {
            return;
        };

        let (yaw, pitch) = match view {
            CameraView::Front => (0.0, 0.0),
            CameraView::Right => (std::f32::consts::FRAC_PI_2, 0.0),
            CameraView::Top => (0.0, std::f32::consts::FRAC_PI_2 - 0.001),
        };

        pan_orbit.target_yaw = yaw;
        pan_orbit.target_pitch = pitch;
    }

    fn toggle_camera_orthographic(&mut self, world: &mut World) {
        use nightshade::ecs::camera::components::{OrthographicCamera, PerspectiveCamera};

        let Some(camera_entity) = world.resources.active_camera else {
            return;
        };

        let Some(camera) = world.get_camera_mut(camera_entity) else {
            return;
        };

        camera.projection = match &camera.projection {
            Projection::Perspective(persp) => Projection::Orthographic(OrthographicCamera {
                x_mag: 10.0,
                y_mag: 10.0,
                z_near: persp.z_near,
                z_far: persp.z_far.unwrap_or(1000.0),
            }),
            Projection::Orthographic(ortho) => Projection::Perspective(PerspectiveCamera {
                y_fov_rad: 45.0_f32.to_radians(),
                aspect_ratio: None,
                z_near: ortho.z_near,
                z_far: Some(ortho.z_far),
            }),
        };
    }

    fn update_gizmo(&mut self, world: &mut World) {
        use nightshade::ecs::gizmos;
        use nightshade::ecs::picking::queries::{PickingOptions, pick_entities};

        if self.context.gizmo.is_none() {
            let dummy_entity = world.spawn_entities(
                LOCAL_TRANSFORM | LOCAL_TRANSFORM_DIRTY | GLOBAL_TRANSFORM,
                1,
            )[0];

            let gizmo = match self.context.gizmo_mode {
                nightshade::ecs::gizmos::GizmoMode::LocalTranslation
                | nightshade::ecs::gizmos::GizmoMode::GlobalTranslation => {
                    gizmos::create_translation_gizmo(world, dummy_entity, self.context.gizmo_mode)
                }
                nightshade::ecs::gizmos::GizmoMode::Rotation => {
                    gizmos::create_rotation_gizmo(world, dummy_entity)
                }
                nightshade::ecs::gizmos::GizmoMode::Scale => {
                    gizmos::create_scale_gizmo(world, dummy_entity)
                }
            };

            world.queue_command(WorldCommand::DespawnRecursive {
                entity: dummy_entity,
            });

            world.add_components(gizmo.root, PARENT | IGNORE_PARENT_SCALE);
            self.set_gizmo_visible(world, &gizmo, false);
            gizmos::set_gizmo_drag_visibility(
                world,
                &gizmo,
                nightshade::ecs::gizmos::GizmoDragMode::None,
            );
            world.update_parent(gizmo.root, None);

            self.context.gizmo = Some(gizmo);
        }

        if let Some(gizmo) = &self.context.gizmo {
            if let Some(selected_entity) = self.context.selected_entity {
                if world.resources.active_camera == Some(selected_entity) {
                    world.update_parent(gizmo.root, None);
                    self.set_gizmo_visible(world, gizmo, false);
                    self.context.hover_axis = None;
                    self.context.drag_mode = nightshade::ecs::gizmos::GizmoDragMode::None;

                    return;
                }

                let current_parent = world.get_parent(gizmo.root).and_then(|p| p.0);
                if current_parent != Some(selected_entity) {
                    world.update_parent(gizmo.root, Some(Parent(Some(selected_entity))));
                }

                let modal_op = self.context.modal_transform.operation;
                let in_modal_transform = modal_op != views::TransformOperation::None;

                if in_modal_transform {
                    let needs_gizmo_switch = match modal_op {
                        views::TransformOperation::Grab => !matches!(
                            self.context.gizmo_mode,
                            nightshade::ecs::gizmos::GizmoMode::LocalTranslation
                                | nightshade::ecs::gizmos::GizmoMode::GlobalTranslation
                        ),
                        views::TransformOperation::Rotate => {
                            self.context.gizmo_mode != nightshade::ecs::gizmos::GizmoMode::Rotation
                        }
                        views::TransformOperation::Scale => {
                            self.context.gizmo_mode != nightshade::ecs::gizmos::GizmoMode::Scale
                        }
                        views::TransformOperation::None => false,
                    };

                    if needs_gizmo_switch {
                        let new_mode = match modal_op {
                            views::TransformOperation::Grab => {
                                nightshade::ecs::gizmos::GizmoMode::LocalTranslation
                            }
                            views::TransformOperation::Rotate => {
                                nightshade::ecs::gizmos::GizmoMode::Rotation
                            }
                            views::TransformOperation::Scale => {
                                nightshade::ecs::gizmos::GizmoMode::Scale
                            }
                            views::TransformOperation::None => self.context.gizmo_mode,
                        };
                        self.context.gizmo_mode = new_mode;
                        self.recreate_gizmo_for_mode(world);
                        return;
                    }

                    self.set_gizmo_visible(world, gizmo, true);

                    let drag_mode = match self.context.modal_transform.constraint {
                        views::TransformConstraint::None => {
                            nightshade::ecs::gizmos::GizmoDragMode::Free
                        }
                        views::TransformConstraint::X => {
                            nightshade::ecs::gizmos::GizmoDragMode::Axis(Vec3::x())
                        }
                        views::TransformConstraint::Y => {
                            nightshade::ecs::gizmos::GizmoDragMode::Axis(Vec3::y())
                        }
                        views::TransformConstraint::Z => {
                            nightshade::ecs::gizmos::GizmoDragMode::Axis(Vec3::z())
                        }
                    };
                    gizmos::set_gizmo_drag_visibility(world, gizmo, drag_mode);

                    let highlight_axis = match self.context.modal_transform.constraint {
                        views::TransformConstraint::None => None,
                        views::TransformConstraint::X => Some(Vec3::x()),
                        views::TransformConstraint::Y => Some(Vec3::y()),
                        views::TransformConstraint::Z => Some(Vec3::z()),
                    };
                    gizmos::highlight_gizmo_axis(world, gizmo, highlight_axis);
                } else {
                    self.set_gizmo_visible(world, gizmo, true);
                    gizmos::set_gizmo_drag_visibility(world, gizmo, self.context.drag_mode);
                }
            } else {
                world.update_parent(gizmo.root, None);
                self.set_gizmo_visible(world, gizmo, false);
                self.context.hover_axis = None;
                self.context.drag_mode = nightshade::ecs::gizmos::GizmoDragMode::None;

                return;
            }
        }

        if let Some(gizmo) = &self.context.gizmo
            && let Some(selected_entity) = self.context.selected_entity
        {
            if let Some(camera_entity) = world.resources.active_camera
                && let (Some(camera_transform), Some(target_transform)) = (
                    world.get_global_transform(camera_entity),
                    world.get_global_transform(selected_entity),
                )
            {
                let camera_pos = nalgebra_glm::vec3(
                    camera_transform.0[(0, 3)],
                    camera_transform.0[(1, 3)],
                    camera_transform.0[(2, 3)],
                );
                let target_pos = nalgebra_glm::vec3(
                    target_transform.0[(0, 3)],
                    target_transform.0[(1, 3)],
                    target_transform.0[(2, 3)],
                );
                let distance = nalgebra_glm::distance(&camera_pos, &target_pos);
                self.context.camera_distance = distance;

                let scale_factor = 0.08;
                let min_scale = 0.3;
                let max_scale = 50.0;
                let scale = (distance * scale_factor).max(min_scale).min(max_scale);

                if let Some(transform) = world.get_local_transform(gizmo.root) {
                    let mut new_transform = *transform;
                    new_transform.translation = Vec3::zeros();
                    new_transform.scale = nalgebra_glm::vec3(scale, scale, scale);
                    new_transform.rotation = Quat::identity();
                    world.assign_local_transform(gizmo.root, new_transform);
                }
            }

            let in_modal =
                self.context.modal_transform.operation != views::TransformOperation::None;
            if in_modal {
                return;
            }

            if world.resources.user_interface.consumed_event {
                if !matches!(
                    self.context.drag_mode,
                    nightshade::ecs::gizmos::GizmoDragMode::None
                ) {
                    if let Some(transform) = world.get_local_transform(selected_entity) {
                        self.context
                            .undo_history
                            .commit_transform_change(selected_entity, *transform);
                    }
                    self.context.drag_mode = nightshade::ecs::gizmos::GizmoDragMode::None;
                    self.context.hover_axis = None;
                    gizmos::set_gizmo_drag_visibility(
                        world,
                        gizmo,
                        nightshade::ecs::gizmos::GizmoDragMode::None,
                    );
                }
                return;
            }

            let mouse = &world.resources.input.mouse;
            let mouse_pos = mouse.position;
            let mouse_pressed = mouse.state.contains(MouseState::LEFT_CLICKED);
            let mouse_just_pressed = mouse.state.contains(MouseState::LEFT_JUST_PRESSED);

            let is_dragging = !matches!(
                self.context.drag_mode,
                nightshade::ecs::gizmos::GizmoDragMode::None
            );
            let mouse_in_selected_viewport = self.is_mouse_in_selected_viewport(world);

            if !mouse_in_selected_viewport && !is_dragging {
                if self.context.hover_axis.is_some() {
                    self.context.hover_axis = None;
                    gizmos::highlight_gizmo_axis(world, gizmo, None);
                }
                return;
            }

            if !is_dragging {
                let all_picked = pick_entities(world, mouse_pos, PickingOptions::default());
                let hover_axis = all_picked
                    .iter()
                    .find_map(|result| gizmos::check_gizmo_axis_hit(world, gizmo, result.entity));

                if hover_axis != self.context.hover_axis {
                    self.context.hover_axis = hover_axis;
                    gizmos::highlight_gizmo_axis(world, gizmo, hover_axis);
                }

                if mouse_just_pressed && let Some(axis) = hover_axis {
                    let new_drag_mode = determine_drag_mode(axis);
                    self.context.drag_mode = new_drag_mode;
                    gizmos::set_gizmo_drag_visibility(world, gizmo, new_drag_mode);
                    if let Some(transform) = world.get_local_transform(selected_entity) {
                        self.context
                            .undo_history
                            .begin_transform_change(selected_entity, *transform);
                    }
                }
            } else if mouse_pressed {
                if let Some(camera_entity) = world.resources.active_camera {
                    let mouse_delta = world.resources.input.mouse.raw_mouse_delta;
                    let base_sensitivity = 0.01;
                    let distance_scale = (self.context.camera_distance * 0.1).sqrt().max(0.5);
                    let sensitivity = base_sensitivity * distance_scale;

                    if let Some(camera_transform) = world.get_global_transform(camera_entity) {
                        let camera_right = nalgebra_glm::vec3(
                            camera_transform.0[(0, 0)],
                            camera_transform.0[(1, 0)],
                            camera_transform.0[(2, 0)],
                        )
                        .normalize();
                        let camera_up = nalgebra_glm::vec3(
                            camera_transform.0[(0, 1)],
                            camera_transform.0[(1, 1)],
                            camera_transform.0[(2, 1)],
                        )
                        .normalize();

                        let drag_params = DragParams {
                            mode: self.context.gizmo_mode,
                            camera_right,
                            camera_up,
                            mouse_delta,
                            sensitivity,
                        };

                        match &self.context.drag_mode {
                            nightshade::ecs::gizmos::GizmoDragMode::Axis(axis) => {
                                apply_axis_drag(world, selected_entity, *axis, drag_params);
                            }
                            nightshade::ecs::gizmos::GizmoDragMode::Plane(plane_normal) => {
                                apply_plane_drag(
                                    world,
                                    selected_entity,
                                    *plane_normal,
                                    drag_params,
                                );
                            }
                            nightshade::ecs::gizmos::GizmoDragMode::Free => {
                                apply_free_drag(
                                    world,
                                    selected_entity,
                                    self.context.gizmo_mode,
                                    camera_right,
                                    camera_up,
                                    mouse_delta,
                                    sensitivity,
                                );
                            }
                            nightshade::ecs::gizmos::GizmoDragMode::None => {}
                        }
                    }
                }
            } else {
                if let Some(transform) = world.get_local_transform(selected_entity) {
                    self.context
                        .undo_history
                        .commit_transform_change(selected_entity, *transform);
                }
                self.context.drag_mode = nightshade::ecs::gizmos::GizmoDragMode::None;
                gizmos::set_gizmo_drag_visibility(
                    world,
                    gizmo,
                    nightshade::ecs::gizmos::GizmoDragMode::None,
                );

                let new_hover_axis = if mouse_in_selected_viewport {
                    let all_picked = pick_entities(world, mouse_pos, PickingOptions::default());
                    all_picked.iter().find_map(|result| {
                        gizmos::check_gizmo_axis_hit(world, gizmo, result.entity)
                    })
                } else {
                    None
                };

                if new_hover_axis != self.context.hover_axis {
                    self.context.hover_axis = new_hover_axis;
                    gizmos::highlight_gizmo_axis(world, gizmo, new_hover_axis);
                }
            }
        }
    }

    fn is_mouse_in_selected_viewport(&self, world: &World) -> bool {
        if let Some(viewport_rect) = &world.resources.window.active_viewport_rect {
            let mouse_pos = world.resources.input.mouse.position;
            return viewport_rect.contains(mouse_pos);
        }
        true
    }

    fn load_project_from_bytes(&mut self, world: &mut World, name: &str, data: &[u8]) {
        let lower_name = name.to_lowercase();
        let parse_result: Result<project::EditorProject, String> =
            if lower_name.ends_with(".project.bin") {
                #[cfg(not(target_arch = "wasm32"))]
                {
                    bincode::deserialize(data).map_err(|error| error.to_string())
                }
                #[cfg(target_arch = "wasm32")]
                {
                    Err("Binary project format not supported on web".to_string())
                }
            } else {
                serde_json::from_slice(data).map_err(|error| error.to_string())
            };
        match parse_result {
            Ok(project) => {
                self.clear_scene(world);
                if let Some(active_map) = project.active_map() {
                    match nightshade::ecs::map::spawn_map(world, active_map) {
                        Ok(result) => {
                            for warning in result.warnings {
                                tracing::warn!("{}", warning);
                                #[cfg(not(target_arch = "wasm32"))]
                                self.toast_service.show_toast(warning, ToastLevel::Warning);
                            }
                        }
                        Err(error) => {
                            tracing::error!("Failed to spawn map: {}", error);
                            return;
                        }
                    }
                }
                if let Some(layout) = project.active_layout() {
                    self.context.tile_tree = Some(layout.tree.clone());
                }
                tracing::info!("Loaded project from dropped data");
                self.context.current_project_path = None;
                self.context.current_project_name = project.project_name.clone();
                self.context.selected_entity = None;
                self.project = Some(project);
                self.project_state.is_open = true;
                self.project_state.is_modified = false;
                self.cleanup_gizmo_entities(world);
            }
            Err(error) => {
                tracing::error!("Failed to parse project: {}", error);
            }
        }
    }

    #[cfg(target_arch = "wasm32")]
    fn save_project(&mut self, world: &World) {
        use nightshade::prelude::wasm_bindgen::JsCast;

        self.update_project_from_current_state(world);
        let project: project::EditorProject = if let Some(existing) = &self.project {
            existing.clone()
        } else {
            let map = self.world_to_map(world);
            let mut new_project =
                project::EditorProject::new(self.context.current_project_name.clone());
            new_project.add_map("Untitled".to_string(), map);
            if let Some(tree) = self.context.tile_tree.clone() {
                new_project.add_layout("Default".to_string(), tree);
            }
            new_project
        };
        let json = match serde_json::to_string_pretty(&project) {
            Ok(json) => json,
            Err(error) => {
                tracing::error!("Failed to serialize project: {}", error);
                return;
            }
        };

        let window = match nightshade::prelude::web_sys::window() {
            Some(w) => w,
            None => {
                tracing::error!("No window available");
                return;
            }
        };
        let document = match window.document() {
            Some(d) => d,
            None => {
                tracing::error!("No document available");
                return;
            }
        };

        let array = nightshade::prelude::js_sys::Array::new();
        array.push(&nightshade::prelude::wasm_bindgen::JsValue::from_str(&json));

        let options = nightshade::prelude::web_sys::BlobPropertyBag::new();
        options.set_type("application/json");

        let blob = match nightshade::prelude::web_sys::Blob::new_with_str_sequence_and_options(
            &array, &options,
        ) {
            Ok(b) => b,
            Err(error) => {
                tracing::error!("Failed to create blob: {:?}", error);
                return;
            }
        };

        let url = match nightshade::prelude::web_sys::Url::create_object_url_with_blob(&blob) {
            Ok(u) => u,
            Err(error) => {
                tracing::error!("Failed to create object URL: {:?}", error);
                return;
            }
        };

        let anchor: nightshade::prelude::web_sys::HtmlAnchorElement = match document
            .create_element("a")
            .ok()
            .and_then(|e| e.dyn_into().ok())
        {
            Some(a) => a,
            None => {
                tracing::error!("Failed to create anchor element");
                return;
            }
        };

        anchor.set_href(&url);
        anchor.set_download("project.project.json");
        anchor.click();

        let _ = nightshade::prelude::web_sys::Url::revoke_object_url(&url);
        tracing::info!("Initiated project download");
        self.project_state.is_modified = false;
    }

    #[cfg(target_arch = "wasm32")]
    fn load_project(&mut self, _world: &mut World) {
        use nightshade::prelude::wasm_bindgen::JsCast;
        use nightshade::prelude::wasm_bindgen::prelude::*;

        let window = match nightshade::prelude::web_sys::window() {
            Some(w) => w,
            None => {
                tracing::error!("No window available");
                return;
            }
        };
        let document = match window.document() {
            Some(d) => d,
            None => {
                tracing::error!("No document available");
                return;
            }
        };

        let input: nightshade::prelude::web_sys::HtmlInputElement = match document
            .create_element("input")
            .ok()
            .and_then(|e| e.dyn_into().ok())
        {
            Some(i) => i,
            None => {
                tracing::error!("Failed to create input element");
                return;
            }
        };

        input.set_type("file");
        input.set_accept(".project.json");

        let pending = self.context.pending_project_load.clone();
        let input_for_closure = input.clone();
        let closure = Closure::once(
            Box::new(move |_event: nightshade::prelude::web_sys::Event| {
                let input_clone = input_for_closure;
                let pending_clone = pending;

                wasm_bindgen_futures::spawn_local(async move {
                    if let Some(files) = input_clone.files()
                        && let Some(file) = files.get(0)
                    {
                        let array_buffer_promise = file.array_buffer();
                        match wasm_bindgen_futures::JsFuture::from(array_buffer_promise).await {
                            Ok(array_buffer) => {
                                let uint8_array = js_sys::Uint8Array::new(&array_buffer);
                                let bytes = uint8_array.to_vec();
                                *pending_clone.borrow_mut() = Some(bytes);
                            }
                            Err(error) => {
                                tracing::error!("Failed to read file: {:?}", error);
                            }
                        }
                    }
                });
            }) as Box<dyn FnOnce(_)>,
        );

        input.set_onchange(Some(closure.as_ref().unchecked_ref()));
        closure.forget();
        input.click();
    }

    #[cfg(target_arch = "wasm32")]
    fn process_pending_project_load(&mut self, world: &mut World) {
        let data = self.context.pending_project_load.borrow_mut().take();
        if let Some(bytes) = data {
            self.load_project_from_bytes(world, "project.project.json", &bytes);
        }
    }

    fn add_primitive_popup_ui(&mut self, world: &mut World, ui_context: &egui::Context) {
        let Some(popup_pos) = self.context.add_primitive_popup else {
            return;
        };

        let mut close_popup = false;
        let mut spawned_entity = None;

        egui::Area::new(egui::Id::new("add_primitive_popup"))
            .fixed_pos(egui::pos2(popup_pos.x, popup_pos.y))
            .order(egui::Order::Foreground)
            .show(ui_context, |ui| {
                egui::Frame::popup(ui.style()).show(ui, |ui| {
                    ui.set_min_width(120.0);
                    ui.label("Add Primitive");
                    ui.separator();

                    if ui.button("Cube").clicked() {
                        spawned_entity = Some(spawn_cube_at(world, Vec3::zeros()));
                        close_popup = true;
                    }
                    if ui.button("Sphere").clicked() {
                        spawned_entity = Some(spawn_sphere_at(world, Vec3::zeros()));
                        close_popup = true;
                    }
                    if ui.button("Cylinder").clicked() {
                        spawned_entity = Some(spawn_cylinder_at(world, Vec3::zeros()));
                        close_popup = true;
                    }
                    if ui.button("Cone").clicked() {
                        spawned_entity = Some(spawn_cone_at(world, Vec3::zeros()));
                        close_popup = true;
                    }
                    if ui.button("Torus").clicked() {
                        spawned_entity = Some(spawn_torus_at(world, Vec3::zeros()));
                        close_popup = true;
                    }
                    if ui.button("Plane").clicked() {
                        spawned_entity = Some(spawn_plane_at(world, Vec3::zeros()));
                        close_popup = true;
                    }
                    ui.separator();
                    ui.label("Lighting");
                    ui.separator();
                    if ui.button("Point Light").clicked() {
                        spawned_entity = Some(self.spawn_point_light(world));
                        close_popup = true;
                    }
                    if ui.button("Spot Light").clicked() {
                        spawned_entity = Some(self.spawn_spot_light(world));
                        close_popup = true;
                    }
                    ui.separator();
                    if ui.button("Empty").clicked() {
                        let entities = EntityBuilder::new()
                            .with_name(Name("Empty".to_string()))
                            .with_local_transform(LocalTransform::default())
                            .with_global_transform(GlobalTransform::default())
                            .spawn(world, 1);
                        spawned_entity = Some(entities[0]);
                        close_popup = true;
                    }
                });
            });

        if ui_context.input(|i| i.pointer.any_click()) && !close_popup {
            let pointer_pos = ui_context.input(|i| i.pointer.interact_pos());
            if let Some(pos) = pointer_pos {
                let popup_rect = egui::Rect::from_min_size(
                    egui::pos2(popup_pos.x, popup_pos.y),
                    egui::vec2(150.0, 300.0),
                );
                if !popup_rect.contains(pos) {
                    close_popup = true;
                }
            }
        }

        if ui_context.input(|i| i.key_pressed(egui::Key::Escape)) {
            close_popup = true;
        }

        if close_popup {
            self.context.add_primitive_popup = None;
        }

        if let Some(entity) = spawned_entity {
            let hierarchy = Box::new(views::UndoHistory::capture_hierarchy(world, entity));
            self.context.undo_history.push(
                views::UndoableOperation::EntityCreated {
                    hierarchy,
                    current_entity: entity,
                },
                "Add primitive".to_string(),
            );
            self.context.selected_entity = Some(entity);
            self.project_state.is_modified = true;
        }
    }

    fn entity_context_menu_ui(&mut self, world: &mut World, ui_context: &egui::Context) {
        let Some(popup_pos) = self.context.entity_context_menu else {
            return;
        };

        let Some(entity) = self.context.selected_entity else {
            self.context.entity_context_menu = None;
            return;
        };

        let entity_name = world
            .get_name(entity)
            .map(|n| n.0.clone())
            .unwrap_or_else(|| format!("Entity {}", entity.id));

        let mut close_popup = false;
        let mut action: Option<&str> = None;

        egui::Area::new(egui::Id::new("entity_context_menu"))
            .fixed_pos(egui::pos2(popup_pos.x, popup_pos.y))
            .order(egui::Order::Foreground)
            .show(ui_context, |ui| {
                egui::Frame::popup(ui.style()).show(ui, |ui| {
                    ui.set_min_width(160.0);
                    ui.label(&entity_name);
                    ui.separator();

                    if ui.button("Grab                G").clicked() {
                        action = Some("grab");
                        close_popup = true;
                    }
                    if ui.button("Rotate              R").clicked() {
                        action = Some("rotate");
                        close_popup = true;
                    }
                    if ui.button("Scale               S").clicked() {
                        action = Some("scale");
                        close_popup = true;
                    }
                    ui.separator();
                    if ui.button("Duplicate    Shift+D").clicked() {
                        action = Some("duplicate");
                        close_popup = true;
                    }
                    if ui.button("Delete              X").clicked() {
                        action = Some("delete");
                        close_popup = true;
                    }
                });
            });

        if ui_context.input(|input| input.pointer.any_click()) && !close_popup {
            let pointer_pos = ui_context.input(|input| input.pointer.interact_pos());
            if let Some(pos) = pointer_pos {
                let popup_rect = egui::Rect::from_min_size(
                    egui::pos2(popup_pos.x, popup_pos.y),
                    egui::vec2(180.0, 200.0),
                );
                if !popup_rect.contains(pos) {
                    close_popup = true;
                }
            }
        }

        if ui_context.input(|input| input.key_pressed(egui::Key::Escape)) {
            close_popup = true;
        }

        if close_popup {
            self.context.entity_context_menu = None;
        }

        match action {
            Some("grab") => {
                self.start_modal_transform(world, views::TransformOperation::Grab);
            }
            Some("rotate") => {
                self.start_modal_transform(world, views::TransformOperation::Rotate);
            }
            Some("scale") => {
                self.start_modal_transform(world, views::TransformOperation::Scale);
            }
            Some("duplicate") => {
                self.duplicate_selected_entity(world);
            }
            Some("delete") => {
                self.delete_selected_entity(world);
            }
            _ => {}
        }
    }

    fn spawn_point_light(&self, world: &mut World) -> Entity {
        let entities = EntityBuilder::new()
            .with_name(Name("Point Light".to_string()))
            .with_local_transform(LocalTransform::default())
            .with_global_transform(GlobalTransform::default())
            .spawn(world, 1);
        let entity = entities[0];
        world.set_light(
            entity,
            Light {
                light_type: LightType::Point,
                color: nalgebra_glm::vec3(1.0, 1.0, 1.0),
                intensity: 1.0,
                range: 10.0,
                inner_cone_angle: 0.0,
                outer_cone_angle: 0.0,
                cast_shadows: false,
                shadow_bias: 0.0,
            },
        );
        entity
    }

    fn spawn_spot_light(&self, world: &mut World) -> Entity {
        let entities = EntityBuilder::new()
            .with_name(Name("Spot Light".to_string()))
            .with_local_transform(LocalTransform::default())
            .with_global_transform(GlobalTransform::default())
            .spawn(world, 1);
        let entity = entities[0];
        world.set_light(
            entity,
            Light {
                light_type: LightType::Spot,
                color: nalgebra_glm::vec3(1.0, 1.0, 1.0),
                intensity: 1.0,
                range: 10.0,
                inner_cone_angle: 0.4,
                outer_cone_angle: 0.6,
                cast_shadows: false,
                shadow_bias: 0.0,
            },
        );
        entity
    }

    fn assets_window_ui(&mut self, _world: &mut World, ui_context: &egui::Context) {
        if !self.context.show_assets_window {
            return;
        }

        let mut window_open = self.context.show_assets_window;

        egui::Window::new("Assets")
            .open(&mut window_open)
            .default_width(600.0)
            .default_height(400.0)
            .resizable(true)
            .show(ui_context, |ui| {
                #[cfg(not(target_arch = "wasm32"))]
                {
                    ui.horizontal(|ui| {
                        if ui.button("Scan Assets Directory").clicked() {
                            self.scan_assets_directory();
                        }
                        ui.separator();
                        if ui.button("Reload All").clicked() {
                            self.reload_all_assets();
                        }
                    });
                    ui.separator();
                }

                egui::ScrollArea::vertical()
                    .id_salt("assets_scroll")
                    .show(ui, |ui| {
                        #[cfg(not(target_arch = "wasm32"))]
                        if let Some(ref project) = self.project {
                            let mut all_prefabs: Vec<(String, String, String, bool)> = Vec::new();

                            for (map_name, map) in &project.maps {
                                for (prefab_name, map_prefab) in &map.prefabs {
                                    if let nightshade::ecs::map::MapPrefab::Reference { path } =
                                        map_prefab
                                    {
                                        let exists = std::path::Path::new(path).exists();
                                        all_prefabs.push((
                                            map_name.clone(),
                                            prefab_name.clone(),
                                            path.clone(),
                                            exists,
                                        ));
                                    }
                                }

                                if let Some(nightshade::ecs::map::MapHdrSkybox::Reference {
                                    path,
                                }) = &map.hdr_skybox
                                {
                                    let exists = std::path::Path::new(path).exists();
                                    all_prefabs.push((
                                        map_name.clone(),
                                        "HDR Skybox".to_string(),
                                        path.clone(),
                                        exists,
                                    ));
                                }
                            }

                            if all_prefabs.is_empty() {
                                ui.label("No referenced assets in project");
                            } else {
                                egui::Grid::new("assets_grid")
                                    .num_columns(5)
                                    .striped(true)
                                    .min_col_width(100.0)
                                    .show(ui, |ui| {
                                        ui.strong("Map");
                                        ui.strong("Asset");
                                        ui.strong("Path");
                                        ui.strong("Status");
                                        ui.strong("Actions");
                                        ui.end_row();

                                        for (map_name, prefab_name, path, exists) in &all_prefabs {
                                            ui.label(map_name);
                                            ui.label(prefab_name);

                                            let path_color = if *exists {
                                                egui::Color32::from_rgb(100, 200, 100)
                                            } else {
                                                egui::Color32::from_rgb(200, 100, 100)
                                            };
                                            ui.label(
                                                egui::RichText::new(path).color(path_color).small(),
                                            );

                                            if *exists {
                                                ui.label(
                                                    egui::RichText::new("Found").color(
                                                        egui::Color32::from_rgb(100, 200, 100),
                                                    ),
                                                );
                                            } else {
                                                ui.label(
                                                    egui::RichText::new("Missing").color(
                                                        egui::Color32::from_rgb(200, 100, 100),
                                                    ),
                                                );
                                            }

                                            ui.horizontal(|ui| {
                                                if ui.small_button("...").clicked()
                                                    && let Some(new_path) = rfd::FileDialog::new()
                                                        .add_filter("GLTF/GLB", &["gltf", "glb"])
                                                        .add_filter("HDR", &["hdr"])
                                                        .pick_file()
                                                {
                                                    let new_path_str = new_path
                                                        .canonicalize()
                                                        .ok()
                                                        .and_then(|p| {
                                                            p.to_str().map(|s| s.to_string())
                                                        })
                                                        .unwrap_or_else(|| {
                                                            new_path.display().to_string()
                                                        });
                                                    self.context.pending_asset_update = Some((
                                                        format!("{}:{}", map_name, prefab_name),
                                                        new_path_str,
                                                    ));
                                                }
                                            });

                                            ui.end_row();
                                        }
                                    });
                            }
                        } else {
                            ui.label("No project loaded");
                        }

                        #[cfg(target_arch = "wasm32")]
                        ui.label("Asset management is not available on web");
                    });
            });

        self.context.show_assets_window = window_open;

        if let Some((key, new_path)) = self.context.pending_asset_update.take() {
            self.update_asset_path(&key, &new_path);
        }

        #[cfg(not(target_arch = "wasm32"))]
        if let Some(message) = self.context.pending_info_toast.take() {
            self.toast_service.show_toast(message, ToastLevel::Info);
        }
    }

    fn animations_window_ui(&mut self, world: &mut World, ui_context: &egui::Context) {
        if !self.context.show_animations_window {
            return;
        }

        let mut window_open = self.context.show_animations_window;
        let mut prefab_to_spawn: Option<String> = None;

        let prefab_names: Vec<String> =
            nightshade::ecs::prefab::resources::prefab_cache_names(&world.resources.prefab_cache)
                .cloned()
                .collect();

        let animation_names: Vec<String> =
            nightshade::ecs::prefab::resources::animation_cache_names(
                &world.resources.animation_cache,
            )
            .cloned()
            .collect();

        egui::Window::new("Animations & Prefabs")
            .open(&mut window_open)
            .default_width(400.0)
            .default_height(500.0)
            .resizable(true)
            .show(ui_context, |ui| {
                ui.heading("Cached Prefabs");
                ui.separator();

                if prefab_names.is_empty() {
                    ui.label("No prefabs cached");
                    ui.label("Load FBX files to import prefabs");
                } else {
                    egui::ScrollArea::vertical()
                        .id_salt("prefabs_scroll")
                        .max_height(200.0)
                        .show(ui, |ui| {
                            for name in &prefab_names {
                                ui.horizontal(|ui| {
                                    if ui.button("Spawn").clicked() {
                                        prefab_to_spawn = Some(name.clone());
                                    }
                                    ui.label(name);
                                });
                            }
                        });
                }

                ui.add_space(10.0);
                ui.heading("Cached Animations");
                ui.separator();

                if animation_names.is_empty() {
                    ui.label("No animations cached");
                    ui.label("Load FBX files to import animations");
                } else {
                    ui.label(format!("{} animations available", animation_names.len()));
                    ui.label("Add animations to entities via the Animation Player inspector");
                    ui.add_space(5.0);

                    egui::ScrollArea::vertical()
                        .id_salt("animations_scroll")
                        .max_height(200.0)
                        .show(ui, |ui| {
                            for name in &animation_names {
                                ui.label(format!("  {}", name));
                            }
                        });
                }
            });

        self.context.show_animations_window = window_open;

        if let Some(prefab_name) = prefab_to_spawn
            && let Some(cached) = world.resources.prefab_cache.prefabs.get(&prefab_name)
        {
            let prefab = cached.prefab.clone();
            let animations = cached.animations.clone();
            let skins = cached.skins.clone();
            let source_path = cached.source_path.clone();

            let entity = nightshade::ecs::prefab::spawn_prefab_with_source(
                world,
                &prefab,
                &animations,
                &skins,
                nalgebra_glm::vec3(0.0, 0.0, 0.0),
                source_path.as_deref(),
            );
            self.context.selected_entity = Some(entity);
        }
    }

    fn visual_effects_window_ui(&mut self, world: &mut World, ui_context: &egui::Context) {
        if !self.context.show_visual_effects_window {
            return;
        }

        let mut window_open = self.context.show_visual_effects_window;

        egui::Window::new("Visual Effects")
            .open(&mut window_open)
            .default_width(350.0)
            .default_height(600.0)
            .resizable(true)
            .show(ui_context, |ui| {
                egui::ScrollArea::vertical().show(ui, |ui| {
                    ui.collapsing("Vertex Snapping", |ui| {
                        let mut snap_enabled = world.resources.graphics.vertex_snap.is_some();
                        if ui.checkbox(&mut snap_enabled, "Enable").changed() {
                            if snap_enabled {
                                world.resources.graphics.vertex_snap = Some(VertexSnap::default());
                            } else {
                                world.resources.graphics.vertex_snap = None;
                            }
                        }
                        if let Some(ref mut vertex_snap) = world.resources.graphics.vertex_snap {
                            ui.horizontal(|ui| {
                                ui.label("Resolution X:");
                                ui.add(
                                    egui::Slider::new(&mut vertex_snap.resolution[0], 80.0..=640.0)
                                        .max_decimals(0),
                                );
                            });
                            ui.horizontal(|ui| {
                                ui.label("Resolution Y:");
                                ui.add(
                                    egui::Slider::new(&mut vertex_snap.resolution[1], 60.0..=480.0)
                                        .max_decimals(0),
                                );
                            });
                        }
                    });

                    ui.collapsing("Texture Mapping", |ui| {
                        ui.checkbox(
                            &mut world.resources.graphics.affine_texture_mapping,
                            "Affine Texture Mapping",
                        );
                    });

                    ui.collapsing("Distance Fog", |ui| {
                        let mut fog_enabled = world.resources.graphics.fog.is_some();
                        if ui.checkbox(&mut fog_enabled, "Enable").changed() {
                            if fog_enabled {
                                world.resources.graphics.fog = Some(Fog::default());
                            } else {
                                world.resources.graphics.fog = None;
                            }
                        }
                        if let Some(ref mut fog) = world.resources.graphics.fog {
                            ui.horizontal(|ui| {
                                ui.label("Start:");
                                ui.add(egui::Slider::new(&mut fog.start, 0.5..=10.0));
                            });
                            ui.horizontal(|ui| {
                                ui.label("End:");
                                ui.add(egui::Slider::new(&mut fog.end, 5.0..=50.0));
                            });
                            ui.horizontal(|ui| {
                                ui.label("Color:");
                                ui.color_edit_button_rgb(&mut fog.color);
                            });
                        }
                    });

                    ui.collapsing("Post-Processing", |ui| {
                        ui.checkbox(&mut self.context.grayscale_enabled, "Grayscale");
                    });

                    ui.collapsing("Color Grading", |ui| {
                        let color_grading = &mut world.resources.graphics.color_grading;

                        ui.horizontal(|ui| {
                            ui.label("Preset:");
                            ui.label(color_grading.preset.name());
                        });

                        ui.horizontal_wrapped(|ui| {
                            for preset in ColorGradingPreset::ALL {
                                if *preset == ColorGradingPreset::Custom {
                                    continue;
                                }
                                let is_selected = color_grading.preset == *preset;
                                if ui.selectable_label(is_selected, preset.name()).clicked() {
                                    *color_grading = preset.to_color_grading();
                                }
                            }
                        });

                        ui.add_space(4.0);

                        ui.horizontal(|ui| {
                            ui.label("Tonemap:");
                            egui::ComboBox::from_id_salt("tonemap_algorithm_ve")
                                .selected_text(color_grading.tonemap_algorithm.name())
                                .show_ui(ui, |ui| {
                                    for algorithm in TonemapAlgorithm::ALL {
                                        if ui
                                            .selectable_value(
                                                &mut color_grading.tonemap_algorithm,
                                                *algorithm,
                                                algorithm.name(),
                                            )
                                            .changed()
                                        {
                                            color_grading.preset = ColorGradingPreset::Custom;
                                        }
                                    }
                                });
                        });

                        ui.horizontal(|ui| {
                            ui.label("Gamma:");
                            if ui
                                .add(
                                    egui::Slider::new(&mut color_grading.gamma, 1.0..=3.0)
                                        .fixed_decimals(2),
                                )
                                .changed()
                            {
                                color_grading.preset = ColorGradingPreset::Custom;
                            }
                        });

                        ui.horizontal(|ui| {
                            ui.label("Saturation:");
                            if ui
                                .add(
                                    egui::Slider::new(&mut color_grading.saturation, 0.0..=2.0)
                                        .fixed_decimals(2),
                                )
                                .changed()
                            {
                                color_grading.preset = ColorGradingPreset::Custom;
                            }
                        });

                        ui.horizontal(|ui| {
                            ui.label("Brightness:");
                            if ui
                                .add(
                                    egui::Slider::new(&mut color_grading.brightness, -0.5..=0.5)
                                        .fixed_decimals(2),
                                )
                                .changed()
                            {
                                color_grading.preset = ColorGradingPreset::Custom;
                            }
                        });

                        ui.horizontal(|ui| {
                            ui.label("Contrast:");
                            if ui
                                .add(
                                    egui::Slider::new(&mut color_grading.contrast, 0.5..=2.0)
                                        .fixed_decimals(2),
                                )
                                .changed()
                            {
                                color_grading.preset = ColorGradingPreset::Custom;
                            }
                        });
                    });

                    ui.collapsing("Bloom", |ui| {
                        ui.checkbox(&mut world.resources.graphics.bloom_enabled, "Enable");
                        ui.add_enabled_ui(world.resources.graphics.bloom_enabled, |ui| {
                            ui.horizontal(|ui| {
                                ui.label("Intensity:");
                                ui.add(egui::Slider::new(
                                    &mut world.resources.graphics.bloom_intensity,
                                    0.0..=2.0,
                                ));
                            });
                        });
                    });

                    let default_open = world.resources.graphics.depth_of_field.enabled;
                    egui::CollapsingHeader::new("Depth of Field")
                        .default_open(default_open)
                        .show(ui, |ui| {
                            let dof = &mut world.resources.graphics.depth_of_field;

                            ui.checkbox(&mut dof.enabled, "Enable");

                            ui.add_enabled_ui(dof.enabled, |ui| {
                                ui.horizontal(|ui| {
                                    ui.label("Focus Distance:");
                                    ui.add(
                                        egui::Slider::new(&mut dof.focus_distance, 0.5..=100.0)
                                            .logarithmic(true),
                                    );
                                });

                                ui.horizontal(|ui| {
                                    ui.label("Focus Range:");
                                    ui.add(
                                        egui::Slider::new(&mut dof.focus_range, 0.1..=50.0)
                                            .logarithmic(true),
                                    );
                                });

                                ui.horizontal(|ui| {
                                    ui.label("Max Blur:");
                                    ui.add(egui::Slider::new(&mut dof.max_blur_radius, 1.0..=20.0));
                                });

                                ui.horizontal(|ui| {
                                    ui.label("Bokeh Threshold:");
                                    ui.add(egui::Slider::new(&mut dof.bokeh_threshold, 0.0..=1.0));
                                });

                                ui.horizontal(|ui| {
                                    ui.label("Bokeh Intensity:");
                                    ui.add(egui::Slider::new(&mut dof.bokeh_intensity, 0.0..=3.0));
                                });

                                ui.horizontal(|ui| {
                                    ui.label("Quality:");
                                    egui::ComboBox::from_id_salt("dof_quality_ve")
                                        .selected_text(dof.quality.name())
                                        .show_ui(ui, |ui| {
                                            for quality in DepthOfFieldQuality::ALL {
                                                ui.selectable_value(
                                                    &mut dof.quality,
                                                    *quality,
                                                    quality.name(),
                                                );
                                            }
                                        });
                                });

                                ui.checkbox(&mut dof.visualize_coc, "Visualize CoC");

                                ui.separator();
                                ui.label("Tilt Shift");

                                ui.checkbox(&mut dof.tilt_shift_enabled, "Enable Tilt Shift");

                                ui.add_enabled_ui(dof.tilt_shift_enabled, |ui| {
                                    ui.horizontal(|ui| {
                                        ui.label("Angle:");
                                        ui.add(
                                            egui::Slider::new(
                                                &mut dof.tilt_shift_angle,
                                                -90.0..=90.0,
                                            )
                                            .suffix("°"),
                                        );
                                    });

                                    ui.horizontal(|ui| {
                                        ui.label("Center:");
                                        ui.add(egui::Slider::new(
                                            &mut dof.tilt_shift_center,
                                            -1.0..=1.0,
                                        ));
                                    });

                                    ui.horizontal(|ui| {
                                        ui.label("Blur Amount:");
                                        ui.add(egui::Slider::new(
                                            &mut dof.tilt_shift_blur_amount,
                                            0.1..=3.0,
                                        ));
                                    });

                                    ui.checkbox(
                                        &mut dof.visualize_tilt_shift,
                                        "Visualize Focus Band",
                                    );
                                });

                                ui.separator();
                                ui.label("Presets:");
                                ui.horizontal_wrapped(|ui| {
                                    if ui.button("Portrait").clicked() {
                                        *dof = DepthOfField::portrait();
                                    }
                                    if ui.button("Cinematic").clicked() {
                                        *dof = DepthOfField::cinematic();
                                    }
                                    if ui.button("Macro").clicked() {
                                        *dof = DepthOfField::macro_shot();
                                    }
                                    if ui.button("Landscape").clicked() {
                                        *dof = DepthOfField::landscape();
                                    }
                                    if ui.button("Tilt Shift").clicked() {
                                        *dof = DepthOfField::tilt_shift();
                                    }
                                });
                            });
                        });
                });
            });

        self.context.show_visual_effects_window = window_open;
    }

    fn update_asset_path(&mut self, key: &str, new_path: &str) {
        let parts: Vec<&str> = key.splitn(2, ':').collect();
        if parts.len() != 2 {
            return;
        }
        let map_name = parts[0];
        let prefab_name = parts[1];

        if let Some(ref mut project) = self.project
            && let Some(map) = project.maps.get_mut(map_name)
        {
            if prefab_name == "HDR Skybox"
                && let Some(nightshade::ecs::map::MapHdrSkybox::Reference { path }) =
                    &mut map.hdr_skybox
            {
                *path = new_path.to_string();
                self.project_state.is_modified = true;
                #[cfg(not(target_arch = "wasm32"))]
                self.toast_service.show_toast(
                    format!("Updated HDR skybox path in map '{}'", map_name),
                    ToastLevel::Success,
                );
                #[cfg(target_arch = "wasm32")]
                tracing::info!("Updated HDR skybox path in map '{}'", map_name);
            } else if let Some(nightshade::ecs::map::MapPrefab::Reference { path }) =
                map.prefabs.get_mut(prefab_name)
            {
                *path = new_path.to_string();
                self.project_state.is_modified = true;
                #[cfg(not(target_arch = "wasm32"))]
                self.toast_service.show_toast(
                    format!("Updated path for '{}' in map '{}'", prefab_name, map_name),
                    ToastLevel::Success,
                );
                #[cfg(target_arch = "wasm32")]
                tracing::info!("Updated path for '{}' in map '{}'", prefab_name, map_name);
            }
        }
    }

    #[cfg(not(target_arch = "wasm32"))]
    fn scan_assets_directory(&mut self) {
        let Some(project_path) = &self.context.current_project_path else {
            self.toast_service
                .show_toast("No project path set", ToastLevel::Warning);
            return;
        };

        let Some(project_dir) = project_path.parent() else {
            self.toast_service
                .show_toast("Could not determine project directory", ToastLevel::Warning);
            return;
        };

        let assets_dir = project_dir.join("assets");
        if !assets_dir.exists() {
            self.toast_service.show_toast(
                format!("Assets directory not found: {}", assets_dir.display()),
                ToastLevel::Warning,
            );
            return;
        }

        let mut found_assets: Vec<std::path::PathBuf> = Vec::new();

        if let Ok(entries) = std::fs::read_dir(&assets_dir) {
            for entry in entries.flatten() {
                let path = entry.path();
                if let Some(ext) = path.extension() {
                    let ext_lower = ext.to_string_lossy().to_lowercase();
                    if ext_lower == "gltf" || ext_lower == "glb" {
                        found_assets.push(path);
                    }
                }
            }
        }

        if found_assets.is_empty() {
            self.toast_service.show_toast(
                "No GLTF/GLB files found in assets directory",
                ToastLevel::Info,
            );
            return;
        }

        let existing_paths: std::collections::HashSet<String> =
            if let Some(ref project) = self.project {
                project
                    .maps
                    .values()
                    .flat_map(|map| {
                        map.prefabs.values().filter_map(|prefab| {
                            if let nightshade::ecs::map::MapPrefab::Reference { path } = prefab {
                                Some(path.clone())
                            } else {
                                None
                            }
                        })
                    })
                    .collect()
            } else {
                std::collections::HashSet::new()
            };

        let mut imported_count = 0;
        for asset_path in found_assets {
            let path_str = asset_path
                .canonicalize()
                .ok()
                .and_then(|p| p.to_str().map(|s| s.to_string()))
                .unwrap_or_else(|| asset_path.display().to_string());

            if existing_paths.contains(&path_str) {
                continue;
            }

            match nightshade::ecs::prefab::import_gltf_from_path(&asset_path) {
                Ok(result) => {
                    for prefab in result.prefabs {
                        self.context.tile_behavior.prefabs.push(PrefabMetadata {
                            prefab,
                            animations: result.animations.clone(),
                            source_path: Some(path_str.clone()),
                        });
                        imported_count += 1;
                    }
                }
                Err(error) => {
                    tracing::warn!("Failed to import {}: {}", asset_path.display(), error);
                }
            }
        }

        if imported_count > 0 {
            self.toast_service.show_toast(
                format!(
                    "Imported {} new prefabs from assets directory",
                    imported_count
                ),
                ToastLevel::Success,
            );
        } else {
            self.toast_service.show_toast(
                "No new assets to import (all already loaded)",
                ToastLevel::Info,
            );
        }
    }

    #[cfg(not(target_arch = "wasm32"))]
    fn reload_all_assets(&mut self) {
        let Some(ref project) = self.project else {
            self.toast_service
                .show_toast("No project loaded", ToastLevel::Warning);
            return;
        };

        let mut paths_to_reload: Vec<String> = Vec::new();

        for map in project.maps.values() {
            for map_prefab in map.prefabs.values() {
                if let nightshade::ecs::map::MapPrefab::Reference { path } = map_prefab
                    && !paths_to_reload.contains(path)
                {
                    paths_to_reload.push(path.clone());
                }
            }
        }

        let mut reloaded_count = 0;
        let mut error_count = 0;

        for path_str in paths_to_reload {
            let path = std::path::Path::new(&path_str);
            if !path.exists() {
                error_count += 1;
                continue;
            }

            match nightshade::ecs::prefab::import_gltf_from_path(path) {
                Ok(result) => {
                    for prefab in result.prefabs {
                        let already_exists = self
                            .context
                            .tile_behavior
                            .prefabs
                            .iter()
                            .any(|meta| meta.prefab.name == prefab.name);

                        if !already_exists {
                            self.context.tile_behavior.prefabs.push(PrefabMetadata {
                                prefab,
                                animations: result.animations.clone(),
                                source_path: Some(path_str.clone()),
                            });
                        }
                    }
                    reloaded_count += 1;
                }
                Err(error) => {
                    tracing::warn!("Failed to reload {}: {}", path_str, error);
                    error_count += 1;
                }
            }
        }

        if reloaded_count > 0 || error_count > 0 {
            let message = if error_count > 0 {
                format!(
                    "Reloaded {} assets ({} errors)",
                    reloaded_count, error_count
                )
            } else {
                format!("Reloaded {} assets", reloaded_count)
            };
            let level = if error_count > 0 {
                ToastLevel::Warning
            } else {
                ToastLevel::Success
            };
            self.toast_service.show_toast(message, level);
        } else {
            self.toast_service
                .show_toast("No assets to reload", ToastLevel::Info);
        }
    }

    fn world_to_map(&self, world: &World) -> nightshade::ecs::map::Map {
        use nightshade::ecs::map::{Map, MapHdrSkybox};

        let mut map = Map::new("Editor Map");
        map.atmosphere = world.resources.graphics.atmosphere;

        if let Some(hdr_path) = &self.context.hdr_skybox_path {
            map.hdr_skybox = Some(MapHdrSkybox::Reference {
                path: hdr_path.to_string_lossy().to_string(),
            });
        }

        let camera_entity = world.resources.active_camera;
        let gizmo_root = self.context.gizmo.as_ref().map(|gizmo| gizmo.root);

        let gizmo_entities: std::collections::HashSet<Entity> = if let Some(gizmo_root_entity) =
            gizmo_root
        {
            let mut entities = std::collections::HashSet::new();
            entities.insert(gizmo_root_entity);
            let descendants =
                nightshade::ecs::transform::queries::query_descendants(world, gizmo_root_entity);
            for descendant in descendants {
                entities.insert(descendant);
            }
            entities
        } else {
            std::collections::HashSet::new()
        };

        let root_entities: Vec<Entity> = world
            .get_all_entities()
            .into_iter()
            .filter(|entity| {
                if Some(*entity) == camera_entity {
                    return false;
                }
                if gizmo_entities.contains(entity) {
                    return false;
                }
                if let Some(parent) = world.get_parent(*entity) {
                    parent.0.is_none()
                } else {
                    true
                }
            })
            .collect();

        for entity in root_entities {
            entity_to_map_node(
                world,
                entity,
                &mut map,
                None,
                camera_entity,
                &gizmo_entities,
            );
        }

        if !world.resources.navmesh.is_empty() {
            map.navmesh = Some(nightshade::ecs::map::MapNavMesh::from_navmesh_world(
                &world.resources.navmesh,
            ));
        }

        map
    }
}

fn entity_to_map_node(
    world: &World,
    entity: Entity,
    map: &mut nightshade::ecs::map::Map,
    parent_node: Option<nightshade::ecs::map::NodeIndex>,
    camera_entity: Option<Entity>,
    gizmo_entities: &std::collections::HashSet<Entity>,
) {
    use nightshade::ecs::map::{
        MapCamera, MapLight, MapMaterial, MapNode, MapPrefab, MeshInstance,
    };

    if Some(entity) == camera_entity {
        return;
    }

    if gizmo_entities.contains(&entity) {
        return;
    }

    let name = world.get_name(entity).map(|n| n.0.clone());

    if let Some(ref entity_name) = name
        && (entity_name.contains("Gizmo") || entity_name.contains("gizmo"))
    {
        return;
    }

    if let Some(render_mesh) = world.get_render_mesh(entity)
        && (render_mesh.name.contains("Gizmo") || render_mesh.name.contains("gizmo"))
    {
        return;
    }

    let transform = world
        .get_local_transform(entity)
        .copied()
        .unwrap_or_default();

    if let Some(prefab_source) = world.get_prefab_source(entity) {
        let entity_node = MapNode::entity_full(name, transform);
        let entity_node_index = if let Some(parent) = parent_node {
            map.add_child_node(parent, entity_node)
        } else {
            map.add_root_node(entity_node)
        };

        let prefab_node = MapNode::prefab(prefab_source.prefab_name.clone());
        map.add_child_node(entity_node_index, prefab_node);

        if let Some(source_path) = &prefab_source.source_path
            && !map.prefabs.contains_key(&prefab_source.prefab_name)
        {
            map.add_prefab(
                prefab_source.prefab_name.clone(),
                MapPrefab::Reference {
                    path: source_path.clone(),
                },
            );
        }

        return;
    }

    let entity_node = MapNode::entity_full(name, transform);

    let entity_node_index = if let Some(parent) = parent_node {
        map.add_child_node(parent, entity_node)
    } else {
        map.add_root_node(entity_node)
    };

    if let Some(light) = world.get_light(entity) {
        let light_node = MapNode::light(MapLight::from(light));
        map.add_child_node(entity_node_index, light_node);
    }

    if let Some(camera) = world.get_camera(entity) {
        let camera_node = MapNode::camera(MapCamera::from(camera));
        map.add_child_node(entity_node_index, camera_node);
    }

    if let Some(render_mesh) = world.get_render_mesh(entity) {
        let material = world
            .get_material_ref(entity)
            .and_then(|mr| {
                registry_entry_by_name(&world.resources.material_registry.registry, &mr.name)
            })
            .map(MapMaterial::from);
        let casts_shadow = world.get_casts_shadow(entity).is_some();
        let bounding_volume = world.get_bounding_volume(entity).copied();

        let mesh_node = MapNode::mesh_full(
            render_mesh.name.clone(),
            material,
            casts_shadow,
            bounding_volume,
            None,
        );
        map.add_child_node(entity_node_index, mesh_node);
    }

    if let Some(instanced_mesh) = world.get_instanced_mesh(entity) {
        let material = world
            .get_material_ref(entity)
            .and_then(|mr| {
                registry_entry_by_name(&world.resources.material_registry.registry, &mr.name)
            })
            .map(MapMaterial::from);
        let casts_shadow = world.get_casts_shadow(entity).is_some();
        let bounding_volume = world.get_bounding_volume(entity).copied();

        let instances: Vec<MeshInstance> = instanced_mesh
            .instances
            .iter()
            .map(|inst| {
                MeshInstance::new([inst.translation.x, inst.translation.y, inst.translation.z])
                    .with_rotation([
                        inst.rotation.i,
                        inst.rotation.j,
                        inst.rotation.k,
                        inst.rotation.w,
                    ])
                    .with_scale([inst.scale.x, inst.scale.y, inst.scale.z])
            })
            .collect();

        let instanced_mesh_node = MapNode::instanced_mesh_full(
            instanced_mesh.mesh_name.clone(),
            instances,
            material,
            casts_shadow,
            bounding_volume,
        );
        map.add_child_node(entity_node_index, instanced_mesh_node);
    }

    if let Some(lines) = world.get_lines(entity)
        && !lines.lines.is_empty()
    {
        let lines_node = MapNode::lines(lines.lines.clone());
        map.add_child_node(entity_node_index, lines_node);
    }

    if let Some(collider) = world.get_collider(entity) {
        use nightshade::ecs::map::{MapBodyType, MapCollider, MapPhysics};
        use nightshade::ecs::physics::ColliderShape;

        let body_type = world
            .get_rigid_body(entity)
            .map(|rb| {
                use nightshade::ecs::physics::types::RigidBodyType;
                match rb.body_type {
                    RigidBodyType::Fixed => MapBodyType::Static,
                    RigidBodyType::KinematicPositionBased
                    | RigidBodyType::KinematicVelocityBased => MapBodyType::Kinematic,
                    RigidBodyType::Dynamic => MapBodyType::Dynamic,
                }
            })
            .unwrap_or(MapBodyType::Static);

        let map_collider = match &collider.shape {
            ColliderShape::Cuboid { hx, hy, hz } => MapCollider::Cuboid {
                half_extents: [*hx, *hy, *hz],
            },
            ColliderShape::Ball { radius } => MapCollider::Ball { radius: *radius },
            ColliderShape::Cylinder {
                half_height,
                radius,
            } => MapCollider::Cylinder {
                half_height: *half_height,
                radius: *radius,
            },
            ColliderShape::Capsule {
                half_height,
                radius,
            } => MapCollider::Capsule {
                half_height: *half_height,
                radius: *radius,
            },
            ColliderShape::TriMesh { vertices, indices } => MapCollider::TriMesh {
                vertices: vertices.clone(),
                indices: indices.clone(),
            },
            _ => MapCollider::FromMesh,
        };

        let physics = MapPhysics {
            body_type,
            collider: map_collider,
            friction: collider.friction,
            restitution: collider.restitution,
            mass: world.get_rigid_body(entity).map(|rb| rb.mass),
        };

        let physics_node = MapNode::Physics(physics);
        map.add_child_node(entity_node_index, physics_node);
    }

    if let Some(children) = world.resources.children_cache.get(&entity) {
        for &child in children {
            entity_to_map_node(
                world,
                child,
                map,
                Some(entity_node_index),
                camera_entity,
                gizmo_entities,
            );
        }
    }
}

fn ensure_bounding_volumes(world: &mut World) {
    use nightshade::ecs::bounding_volume::components::BoundingVolume;

    let render_mesh_entities: Vec<Entity> = world
        .query_entities(RENDER_MESH)
        .filter(|entity| !world.entity_has_components(*entity, BOUNDING_VOLUME))
        .collect();

    for entity in render_mesh_entities {
        let mesh_name = world
            .get_render_mesh(entity)
            .map(|rm| rm.name.clone())
            .unwrap_or_default();

        let bounding_volume = if let Some(mesh) =
            registry_entry_by_name(&world.resources.mesh_cache.registry, &mesh_name)
        {
            if let Some(bv) = &mesh.bounding_volume {
                *bv
            } else {
                BoundingVolume::from_mesh_type(&mesh_name)
            }
        } else {
            BoundingVolume::from_mesh_type(&mesh_name)
        };

        world.add_components(entity, BOUNDING_VOLUME);
        world.set_bounding_volume(entity, bounding_volume);
    }

    let instanced_mesh_entities: Vec<Entity> = world
        .query_entities(INSTANCED_MESH)
        .filter(|entity| !world.entity_has_components(*entity, BOUNDING_VOLUME))
        .collect();

    for entity in instanced_mesh_entities {
        let mesh_name = world
            .get_instanced_mesh(entity)
            .map(|im| im.mesh_name.clone())
            .unwrap_or_default();

        let bounding_volume = if let Some(mesh) =
            registry_entry_by_name(&world.resources.mesh_cache.registry, &mesh_name)
        {
            if let Some(bv) = &mesh.bounding_volume {
                *bv
            } else {
                BoundingVolume::from_mesh_type(&mesh_name)
            }
        } else {
            BoundingVolume::from_mesh_type(&mesh_name)
        };

        world.add_components(entity, BOUNDING_VOLUME);
        world.set_bounding_volume(entity, bounding_volume);
    }
}

fn query_number_of_lines(world: &World) -> usize {
    world
        .query_entities(LINES)
        .map(|entity| {
            world
                .get_lines(entity)
                .map(|lines| lines.lines.len())
                .unwrap_or(0)
        })
        .sum::<usize>()
}

fn spawn_lines(world: &mut World) {
    const LINE_COUNT: usize = 2_000;

    let entity = world.spawn_entities(
        LINES | NAME | LOCAL_TRANSFORM | GLOBAL_TRANSFORM | LOCAL_TRANSFORM_DIRTY | VISIBILITY,
        1,
    )[0];

    if let Some(name) = world.get_name_mut(entity) {
        *name = Name(format!("{} Lines", LINE_COUNT));
    }

    if let Some(transform) = world.get_local_transform_mut(entity) {
        *transform = LocalTransform::default();
    }

    if let Some(lines) = world.get_lines_mut(entity) {
        let mut test_lines = Vec::new();

        (0..LINE_COUNT).for_each(|index| {
            let angle = (index as f32 / LINE_COUNT as f32) * std::f32::consts::PI * 20.0;
            let radius = 5.0 + (index as f32 / LINE_COUNT as f32) * 50.0;
            let height = (index as f32 / 100.0).sin() * 20.0;

            let start = Vec3::new(radius * angle.cos(), height, radius * angle.sin());
            let end = Vec3::new(
                radius * (angle + 0.05).cos(),
                height + 2.0,
                radius * (angle + 0.05).sin(),
            );

            let hue = (index as f32 / LINE_COUNT as f32) * 360.0;
            let color = hue_to_rgb(hue);

            test_lines.push(Line {
                start,
                end,
                color: Vec4::new(color.x, color.y, color.z, 1.0),
            });
        });

        lines.lines = test_lines;
    }
}

fn hue_to_rgb(hue: f32) -> Vec3 {
    let h = hue / 60.0;
    let c = 1.0;
    let x = c * (1.0 - ((h % 2.0) - 1.0).abs());

    let (r, g, b) = match h as i32 {
        0 => (c, x, 0.0),
        1 => (x, c, 0.0),
        2 => (0.0, c, x),
        3 => (0.0, x, c),
        4 => (x, 0.0, c),
        _ => (c, 0.0, x),
    };

    Vec3::new(r, g, b)
}

fn spawn_meshes(world: &mut World) {
    const MESH_COUNT: usize = 10_000;
    const GRID_SIZE: usize = 22;
    const SPACING: f32 = 2.0;

    let parent_entity = world.spawn_entities(
        NAME | LOCAL_TRANSFORM | LOCAL_TRANSFORM_DIRTY | GLOBAL_TRANSFORM,
        1,
    )[0];

    world.set_name(parent_entity, Name("10,000 Meshes Group".to_string()));
    world.set_local_transform(
        parent_entity,
        LocalTransform {
            translation: Vec3::new(0.0, 0.0, 0.0),
            rotation: Quat::identity(),
            scale: Vec3::new(1.0, 1.0, 1.0),
        },
    );
    world.set_local_transform_dirty(parent_entity, LocalTransformDirty);
    world.set_global_transform(parent_entity, GlobalTransform::default());

    let mut mesh_count = 0;
    let mesh_types = ["Cube", "Sphere", "Cylinder", "Cone", "Torus", "Plane"];
    let offset = -(GRID_SIZE as f32 * SPACING) / 2.0;
    let y_offset = 1.0;

    'outer: for x in 0..GRID_SIZE {
        for y in 0..GRID_SIZE {
            for z in 0..GRID_SIZE {
                if mesh_count >= MESH_COUNT {
                    break 'outer;
                }

                let entity = world.spawn_entities(
                    NAME | LOCAL_TRANSFORM
                        | LOCAL_TRANSFORM_DIRTY
                        | GLOBAL_TRANSFORM
                        | RENDER_MESH
                        | MATERIAL_REF
                        | VISIBILITY
                        | CASTS_SHADOW
                        | PARENT
                        | BOUNDING_VOLUME,
                    1,
                )[0];

                world.update_parent(entity, Some(Parent(Some(parent_entity))));

                world.set_name(entity, Name(format!("Mesh {}", mesh_count + 1)));

                world.set_local_transform(
                    entity,
                    LocalTransform {
                        translation: Vec3::new(
                            offset + x as f32 * SPACING,
                            offset + y as f32 * SPACING + y_offset,
                            offset + z as f32 * SPACING,
                        ),
                        rotation: Quat::identity(),
                        scale: Vec3::new(0.5, 0.5, 0.5),
                    },
                );
                world.set_local_transform_dirty(entity, LocalTransformDirty);
                world.set_global_transform(entity, GlobalTransform::default());

                let mesh_type = mesh_types[mesh_count % mesh_types.len()];
                world.set_render_mesh(entity, RenderMesh::new(mesh_type));
                world.set_bounding_volume(entity, BoundingVolume::from_mesh_type(mesh_type));

                let material_name = match mesh_count % 6 {
                    0 => "Red",
                    1 => "Green",
                    2 => "Blue",
                    3 => "Yellow",
                    4 => "Magenta",
                    _ => "Cyan",
                };
                if let Some(&index) = world
                    .resources
                    .material_registry
                    .registry
                    .name_to_index
                    .get(material_name)
                {
                    world
                        .resources
                        .material_registry
                        .registry
                        .add_reference(index);
                }
                world.set_material_ref(entity, MaterialRef::new(material_name.to_string()));

                world.set_visibility(entity, Visibility { visible: true });
                world.set_casts_shadow(entity, CastsShadow);

                mesh_count += 1;
            }
        }
    }

    tracing::info!("Spawned {} meshes in a lattice formation", mesh_count);
}

fn spawn_3d_text(world: &mut World) {
    let text_index = world.resources.text_cache.add_text("3D Text Object");
    let entity = world.spawn_entities(
        NAME | LOCAL_TRANSFORM | LOCAL_TRANSFORM_DIRTY | GLOBAL_TRANSFORM | TEXT | VISIBILITY,
        1,
    )[0];

    world.set_name(entity, Name("3D Text".to_string()));

    world.set_local_transform(
        entity,
        LocalTransform {
            translation: Vec3::new(
                rand::random::<f32>() * 10.0 - 5.0,
                3.0,
                rand::random::<f32>() * 10.0 - 5.0,
            ),
            rotation: Quat::identity(),
            scale: Vec3::new(1.0, 1.0, 1.0),
        },
    );
    world.set_local_transform_dirty(entity, LocalTransformDirty);
    world.set_global_transform(entity, GlobalTransform::default());

    if let Some(text) = world.get_text_mut(entity) {
        text.text_index = text_index;
        text.properties = TextProperties {
            font_size: 32.0,
            color: Vec4::new(
                rand::random::<f32>(),
                rand::random::<f32>(),
                rand::random::<f32>(),
                1.0,
            ),
            alignment: TextAlignment::Center,
            vertical_alignment: VerticalAlignment::Middle,
            ..Default::default()
        };
        text.dirty = true;
    }
}

fn spawn_text_lattice(world: &mut World) {
    const TEXT_COUNT: usize = 5_000;
    const GRID_X: usize = 25;
    const GRID_Y: usize = 20;
    const GRID_Z: usize = 10;
    const SPACING: f32 = 3.0;

    let parent_entity = world.spawn_entities(
        NAME | LOCAL_TRANSFORM | LOCAL_TRANSFORM_DIRTY | GLOBAL_TRANSFORM,
        1,
    )[0];

    world.set_name(
        parent_entity,
        Name("Text Lattice (Stress Test)".to_string()),
    );
    world.set_local_transform(
        parent_entity,
        LocalTransform {
            translation: Vec3::new(0.0, 5.0, 0.0),
            rotation: Quat::identity(),
            scale: Vec3::new(1.0, 1.0, 1.0),
        },
    );
    world.set_local_transform_dirty(parent_entity, LocalTransformDirty);
    world.set_global_transform(parent_entity, GlobalTransform::default());

    let text_variations = [
        "Text", "Hello", "World", "Test", "Lorem", "Ipsum", "3D", "Engine", "Render", "Buffer",
        "Dynamic", "Scale", "Vertex", "Index", "GPU", "SDF",
    ];

    let offset_x = -(GRID_X as f32 * SPACING) / 2.0;
    let offset_y = -(GRID_Y as f32 * SPACING) / 2.0;
    let offset_z = -(GRID_Z as f32 * SPACING) / 2.0;

    let mut text_count = 0;

    'outer: for x in 0..GRID_X {
        for y in 0..GRID_Y {
            for z in 0..GRID_Z {
                if text_count >= TEXT_COUNT {
                    break 'outer;
                }

                let text_content = format!(
                    "{} #{}",
                    text_variations[text_count % text_variations.len()],
                    text_count + 1
                );
                let text_index = world.resources.text_cache.add_text(&text_content);

                let entity = world.spawn_entities(
                    NAME | LOCAL_TRANSFORM
                        | LOCAL_TRANSFORM_DIRTY
                        | GLOBAL_TRANSFORM
                        | TEXT
                        | VISIBILITY
                        | PARENT,
                    1,
                )[0];

                world.set_parent(entity, Parent(Some(parent_entity)));
                world.set_name(entity, Name(format!("Text {}", text_count + 1)));

                world.set_local_transform(
                    entity,
                    LocalTransform {
                        translation: Vec3::new(
                            offset_x + x as f32 * SPACING,
                            offset_y + y as f32 * SPACING,
                            offset_z + z as f32 * SPACING,
                        ),
                        rotation: Quat::identity(),
                        scale: Vec3::new(1.0, 1.0, 1.0),
                    },
                );
                world.set_local_transform_dirty(entity, LocalTransformDirty);
                world.set_global_transform(entity, GlobalTransform::default());

                if let Some(text) = world.get_text_mut(entity) {
                    text.text_index = text_index;
                    text.properties = TextProperties {
                        font_size: 16.0,
                        color: Vec4::new(
                            rand::random::<f32>(),
                            rand::random::<f32>(),
                            rand::random::<f32>(),
                            1.0,
                        ),
                        alignment: TextAlignment::Center,
                        vertical_alignment: VerticalAlignment::Middle,
                        ..Default::default()
                    };
                    text.dirty = true;
                }

                text_count += 1;
            }
        }
    }

    tracing::info!(
        "Spawned {} text entities in a lattice formation",
        text_count
    );
}

fn determine_drag_mode(axis: Vec3) -> nightshade::ecs::gizmos::GizmoDragMode {
    use nightshade::ecs::gizmos::GizmoDragMode;

    if axis.x > 1.05 || axis.y > 1.05 || axis.z > 1.05 {
        let plane_normal = if axis.x > 1.05 && axis.y > 1.05 {
            nalgebra_glm::vec3(0.0, 0.0, 1.0)
        } else if axis.y > 1.05 && axis.z > 1.05 {
            nalgebra_glm::vec3(1.0, 0.0, 0.0)
        } else if axis.x > 1.05 && axis.z > 1.05 {
            nalgebra_glm::vec3(0.0, 1.0, 0.0)
        } else {
            return GizmoDragMode::Free;
        };
        GizmoDragMode::Plane(plane_normal)
    } else {
        let single_axis = if axis.x > 0.99 {
            nalgebra_glm::vec3(1.0, 0.0, 0.0)
        } else if axis.y > 0.99 {
            nalgebra_glm::vec3(0.0, 1.0, 0.0)
        } else if axis.z > 0.99 {
            nalgebra_glm::vec3(0.0, 0.0, 1.0)
        } else {
            return GizmoDragMode::Free;
        };
        GizmoDragMode::Axis(single_axis)
    }
}

struct DragParams {
    mode: nightshade::ecs::gizmos::GizmoMode,
    camera_right: Vec3,
    camera_up: Vec3,
    mouse_delta: Vec2,
    sensitivity: f32,
}

fn rotate_vec3_by_global_transform(transform: &GlobalTransform, v: &Vec3) -> Vec3 {
    let right = nalgebra_glm::vec3(
        transform.0[(0, 0)],
        transform.0[(1, 0)],
        transform.0[(2, 0)],
    );
    let up = nalgebra_glm::vec3(
        transform.0[(0, 1)],
        transform.0[(1, 1)],
        transform.0[(2, 1)],
    );
    let forward = nalgebra_glm::vec3(
        transform.0[(0, 2)],
        transform.0[(1, 2)],
        transform.0[(2, 2)],
    );

    let right_normalized = nalgebra_glm::normalize(&right);
    let up_normalized = nalgebra_glm::normalize(&up);
    let forward_normalized = nalgebra_glm::normalize(&forward);

    right_normalized * v.x + up_normalized * v.y + forward_normalized * v.z
}

fn rotate_vec3_by_inverse_global_transform(transform: &GlobalTransform, v: &Vec3) -> Vec3 {
    let right = nalgebra_glm::vec3(
        transform.0[(0, 0)],
        transform.0[(1, 0)],
        transform.0[(2, 0)],
    );
    let up = nalgebra_glm::vec3(
        transform.0[(0, 1)],
        transform.0[(1, 1)],
        transform.0[(2, 1)],
    );
    let forward = nalgebra_glm::vec3(
        transform.0[(0, 2)],
        transform.0[(1, 2)],
        transform.0[(2, 2)],
    );

    let right_normalized = nalgebra_glm::normalize(&right);
    let up_normalized = nalgebra_glm::normalize(&up);
    let forward_normalized = nalgebra_glm::normalize(&forward);

    nalgebra_glm::vec3(
        nalgebra_glm::dot(&right_normalized, v),
        nalgebra_glm::dot(&up_normalized, v),
        nalgebra_glm::dot(&forward_normalized, v),
    )
}

fn apply_axis_drag(world: &mut World, target: Entity, axis: Vec3, params: DragParams) {
    use nightshade::ecs::gizmos::GizmoMode;

    let DragParams {
        mode,
        camera_right,
        camera_up,
        mouse_delta,
        sensitivity,
    } = params;
    match mode {
        GizmoMode::LocalTranslation | GizmoMode::GlobalTranslation => {
            let world_axis = if let Some(entity_transform) = world.get_global_transform(target) {
                rotate_vec3_by_global_transform(entity_transform, &axis)
            } else {
                axis
            };

            let screen_movement = camera_right * mouse_delta.x - camera_up * mouse_delta.y;
            let movement_amount = nalgebra_glm::dot(&screen_movement, &world_axis) * sensitivity;
            let world_movement = world_axis * movement_amount;

            let local_movement =
                if let Some(parent_entity) = world.get_parent(target).and_then(|p| p.0) {
                    if let Some(parent_transform) = world.get_global_transform(parent_entity) {
                        rotate_vec3_by_inverse_global_transform(parent_transform, &world_movement)
                    } else {
                        world_movement
                    }
                } else {
                    world_movement
                };

            if let Some(transform) = world.get_local_transform(target) {
                let mut new_transform = *transform;
                new_transform.translation += local_movement;
                world.assign_local_transform(target, new_transform);
            }
        }
        GizmoMode::Rotation => {
            let rotation_speed = sensitivity * 2.0;
            let angle = if axis.x > 0.99 {
                -mouse_delta.y * rotation_speed
            } else if axis.y > 0.99 {
                mouse_delta.x * rotation_speed
            } else {
                -mouse_delta.x * rotation_speed
            };

            if angle.abs() > 0.001 {
                let world_rotation = nalgebra_glm::quat_angle_axis(angle, &axis);
                if let Some(transform) = world.get_local_transform(target) {
                    let mut new_transform = *transform;
                    new_transform.rotation = world_rotation * new_transform.rotation;
                    world.assign_local_transform(target, new_transform);
                }
            }
        }
        GizmoMode::Scale => {
            let scale_delta = (mouse_delta.x - mouse_delta.y) * sensitivity;
            if let Some(transform) = world.get_local_transform(target) {
                let mut new_transform = *transform;
                if axis.x > 0.99 {
                    new_transform.scale.x *= 1.0 + scale_delta;
                } else if axis.y > 0.99 {
                    new_transform.scale.y *= 1.0 + scale_delta;
                } else {
                    new_transform.scale.z *= 1.0 + scale_delta;
                }
                new_transform.scale.x = new_transform.scale.x.max(0.01);
                new_transform.scale.y = new_transform.scale.y.max(0.01);
                new_transform.scale.z = new_transform.scale.z.max(0.01);
                world.assign_local_transform(target, new_transform);
            }
        }
    }
}

fn apply_plane_drag(world: &mut World, target: Entity, plane_normal: Vec3, params: DragParams) {
    use nightshade::ecs::gizmos::GizmoMode;

    let DragParams {
        mode,
        camera_right,
        camera_up,
        mouse_delta,
        sensitivity,
    } = params;
    match mode {
        GizmoMode::LocalTranslation | GizmoMode::GlobalTranslation => {
            let world_plane_normal =
                if let Some(entity_transform) = world.get_global_transform(target) {
                    rotate_vec3_by_global_transform(entity_transform, &plane_normal)
                } else {
                    plane_normal
                };

            let screen_movement = camera_right * mouse_delta.x * sensitivity
                - camera_up * mouse_delta.y * sensitivity;

            let world_movement_in_plane = screen_movement
                - world_plane_normal * nalgebra_glm::dot(&screen_movement, &world_plane_normal);

            let local_movement =
                if let Some(parent_entity) = world.get_parent(target).and_then(|p| p.0) {
                    if let Some(parent_transform) = world.get_global_transform(parent_entity) {
                        rotate_vec3_by_inverse_global_transform(
                            parent_transform,
                            &world_movement_in_plane,
                        )
                    } else {
                        world_movement_in_plane
                    }
                } else {
                    world_movement_in_plane
                };

            if let Some(transform) = world.get_local_transform(target) {
                let mut new_transform = *transform;
                new_transform.translation += local_movement;
                world.assign_local_transform(target, new_transform);
            }
        }
        GizmoMode::Rotation => {
            let rotation_speed = sensitivity * 2.0;
            let angle = if plane_normal.z.abs() > 0.99 {
                mouse_delta.x * rotation_speed
            } else if plane_normal.x.abs() > 0.99 {
                -mouse_delta.y * rotation_speed
            } else {
                mouse_delta.x * rotation_speed
            };

            if angle.abs() > 0.001 {
                let world_rotation = nalgebra_glm::quat_angle_axis(angle, &plane_normal);
                if let Some(transform) = world.get_local_transform(target) {
                    let mut new_transform = *transform;
                    new_transform.rotation = world_rotation * new_transform.rotation;
                    world.assign_local_transform(target, new_transform);
                }
            }
        }
        GizmoMode::Scale => {
            let scale_delta = (mouse_delta.x - mouse_delta.y) * sensitivity;
            if let Some(transform) = world.get_local_transform(target) {
                let mut new_transform = *transform;
                if plane_normal.z.abs() > 0.99 {
                    new_transform.scale.x *= 1.0 + scale_delta;
                    new_transform.scale.y *= 1.0 + scale_delta;
                } else if plane_normal.x.abs() > 0.99 {
                    new_transform.scale.y *= 1.0 + scale_delta;
                    new_transform.scale.z *= 1.0 + scale_delta;
                } else {
                    new_transform.scale.x *= 1.0 + scale_delta;
                    new_transform.scale.z *= 1.0 + scale_delta;
                }
                new_transform.scale.x = new_transform.scale.x.max(0.01);
                new_transform.scale.y = new_transform.scale.y.max(0.01);
                new_transform.scale.z = new_transform.scale.z.max(0.01);
                world.assign_local_transform(target, new_transform);
            }
        }
    }
}

fn apply_free_drag(
    world: &mut World,
    target: Entity,
    mode: nightshade::ecs::gizmos::GizmoMode,
    camera_right: Vec3,
    camera_up: Vec3,
    mouse_delta: Vec2,
    sensitivity: f32,
) {
    use nightshade::ecs::gizmos::GizmoMode;

    match mode {
        GizmoMode::LocalTranslation | GizmoMode::GlobalTranslation => {
            let world_movement = camera_right * mouse_delta.x * sensitivity
                - camera_up * mouse_delta.y * sensitivity;

            let local_movement =
                if let Some(parent_entity) = world.get_parent(target).and_then(|p| p.0) {
                    if let Some(parent_transform) = world.get_global_transform(parent_entity) {
                        rotate_vec3_by_inverse_global_transform(parent_transform, &world_movement)
                    } else {
                        world_movement
                    }
                } else {
                    world_movement
                };

            if let Some(transform) = world.get_local_transform(target) {
                let mut new_transform = *transform;
                new_transform.translation += local_movement;
                world.assign_local_transform(target, new_transform);
            }
        }
        GizmoMode::Rotation => {
            let rotation_speed = sensitivity * 2.0;
            let axis_x = nalgebra_glm::vec3(1.0, 0.0, 0.0);
            let axis_y = nalgebra_glm::vec3(0.0, 1.0, 0.0);

            let rotation_x =
                nalgebra_glm::quat_angle_axis(-mouse_delta.y * rotation_speed, &axis_x);
            let rotation_y = nalgebra_glm::quat_angle_axis(mouse_delta.x * rotation_speed, &axis_y);

            if let Some(transform) = world.get_local_transform(target) {
                let mut new_transform = *transform;
                new_transform.rotation = rotation_y * rotation_x * new_transform.rotation;
                world.assign_local_transform(target, new_transform);
            }
        }
        GizmoMode::Scale => {
            let scale_delta = (mouse_delta.x - mouse_delta.y) * sensitivity;
            let scale_factor = 1.0 + scale_delta;

            if let Some(transform) = world.get_local_transform(target) {
                let mut new_transform = *transform;
                new_transform.scale *= scale_factor;
                new_transform.scale.x = new_transform.scale.x.max(0.01);
                new_transform.scale.y = new_transform.scale.y.max(0.01);
                new_transform.scale.z = new_transform.scale.z.max(0.01);
                world.assign_local_transform(target, new_transform);
            }
        }
    }
}
