#![windows_subsystem = "windows"]

extern crate alloc;

mod events;
#[cfg(not(target_arch = "wasm32"))]
mod plugin_runtime;
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;
mod widgets;

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

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

#[derive(Default)]
struct Editor {
    context: EditorContext,
    inspector_ui: ComponentInspectorUi,
    #[cfg(not(target_arch = "wasm32"))]
    plugin_runtime: Option<plugin_runtime::EditorPluginRuntime>,
    #[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::CloudySky;

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

        let camera_position = Vec3::new(0.0, 4.0, 10.0);
        let main_camera = spawn_camera(world, camera_position, "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.load_plugins();
            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;
        }

        #[cfg(not(target_arch = "wasm32"))]
        if let Some(ref mut runtime) = self.plugin_runtime {
            runtime.send_event(&EditorEvent::FrameStart {
                delta_time: world.resources.window.timing.delta_time,
            });
            runtime.call_on_frame();
        }

        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();
        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 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(egui_tiles::Tile::Container(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(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(egui_tiles::Tile::Container(container)) =
                                    tree.tiles.get_mut(root_id)
                            {
                                match container {
                                    egui_tiles::Container::Tabs(tabs) => {
                                        tabs.add_child(new_tile_id);
                                    }
                                    egui_tiles::Container::Linear(linear) => {
                                        linear.add_child(new_tile_id);
                                    }
                                    egui_tiles::Container::Grid(grid) => {
                                        grid.add_child(new_tile_id);
                                    }
                                }
                            }
                        }
                    }
                }
            });

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

        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);

        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);
        let delta_time = world.resources.window.timing.delta_time;
        update_animation_players(world, delta_time);
        apply_animations(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;
        }

        fly_camera_system(world);
        ensure_bounding_volumes(world);
        self.update_picking(world);
        self.update_gizmo(world);
        world.resources.graphics.bounding_volume_selected_entity = self.context.selected_entity;
        world.resources.graphics.show_selected_bounding_volume =
            self.context.selected_entity.is_some();
    }

    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, payload } => {
                if let Some(event) = payload.downcast_ref::<EditorEvent>() {
                    self.handle_editor_event(world, event);
                } else {
                    tracing::warn!("Received unknown app event type: {}", type_name);
                }
            }
        }
    }

    fn on_keyboard_input(&mut self, world: &mut World, key_code: KeyCode, key_state: KeyState) {
        if key_state == KeyState::Pressed {
            match key_code {
                KeyCode::Escape => {
                    if self.context.play_mode {
                        self.exit_play_mode(world);
                    }
                }
                KeyCode::KeyQ => {
                    if !self.context.play_mode {
                        world.resources.window.should_exit = true;
                    }
                }
                _ => {}
            }
        }
    }

    fn configure_render_graph(
        &mut self,
        graph: &mut RenderGraph<World>,
        device: &wgpu::Device,
        surface_format: wgpu::TextureFormat,
        resources: RenderResources,
    ) {
        let compute_grayscale_pass = passes::ComputeGrayscalePass::new(device);
        let _ = graph.add_pass(
            Box::new(compute_grayscale_pass),
            &[
                ("input", resources.scene_color),
                ("output", resources.compute_output),
            ],
        );

        let blit_pass = passes::BlitPass::new(device, surface_format);
        let _ = graph.add_pass(
            Box::new(blit_pass),
            &[
                ("input", resources.compute_output),
                ("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 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 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_pointers(
                                world as *mut World,
                                &mut self.context.selected_entity as *mut Option<Entity>,
                                ui,
                            );
                        });
                        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"))]
                        self.render_plugin_inspectors(ui);
                    });
            });
    }

    #[cfg(not(target_arch = "wasm32"))]
    fn render_plugin_inspectors(&mut self, ui: &mut egui::Ui) {
        let Some(entity) = self.context.selected_entity else {
            return;
        };
        let entity_id = entity.id as u64;

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

        let inspectors = runtime.get_registered_inspectors();
        if inspectors.is_empty() {
            return;
        }

        ui.separator();
        ui.heading("Plugin Inspectors");
        ui.separator();

        for (inspector_id, inspector_name) in inspectors {
            if let Some(elements) = runtime.request_inspector_ui(&inspector_id, entity_id) {
                ui.group(|ui| {
                    ui.label(egui::RichText::new(&inspector_name).strong());
                    ui.separator();
                    plugin_runtime::render_ui_elements(ui, &elements);
                });
                ui.separator();
            }
        }
    }

    #[cfg(not(target_arch = "wasm32"))]
    fn render_plugin_menu_items(&mut self, ui: &mut egui::Ui) {
        let Some(ref mut runtime) = self.plugin_runtime else {
            return;
        };

        let menu_items = runtime.get_registered_menu_items();
        if menu_items.is_empty() {
            return;
        }

        ui.separator();

        let mut modal_request: Option<(String, String)> = None;

        for (item_id, path) in menu_items {
            let label = path.split('/').next_back().unwrap_or(&path);
            if ui.button(label).clicked() {
                if let Some(elements) = runtime.request_menu_ui(&item_id) {
                    let body_text: Vec<String> = elements
                        .iter()
                        .filter_map(|element| {
                            if let nightshade::editor_plugin::UiElement::Label { text } = element {
                                Some(text.clone())
                            } else {
                                None
                            }
                        })
                        .collect();

                    if !body_text.is_empty() {
                        modal_request = Some((label.to_string(), body_text.join("\n")));
                    }
                }
                ui.close();
            }
        }

        if let Some((_title, body)) = modal_request {
            self.toast_service.show_toast(body, ToastLevel::Info);
        }
    }

    fn handle_editor_event(&mut self, _world: &mut World, event: &EditorEvent) {
        #[cfg(not(target_arch = "wasm32"))]
        if let Some(ref mut runtime) = self.plugin_runtime {
            runtime.send_event(event);
        }

        match event {
            EditorEvent::EntitySelected { entity_id } => {
                tracing::info!("Entity selected: {}", entity_id);
            }
            EditorEvent::EntityDeselected => {
                tracing::info!("Entity deselected");
                self.context.selected_entity = None;
            }
            EditorEvent::PanelToggled { panel } => {
                tracing::info!("Panel toggled: {:?}", panel);
            }
            EditorEvent::FrameStart { .. } => {}
        }
    }

    #[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")]
                    {
                        ui.menu_button("File", |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| {
                        let left_panel_before = self.context.show_left_panel;
                        let right_panel_before = self.context.show_right_panel;

                        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();
                        }
                        ui.separator();
                        ui.checkbox(&mut world.resources.graphics.show_grid, "Grid");
                        ui.checkbox(
                            &mut world.resources.graphics.show_bounding_volumes,
                            "Bounding Volumes",
                        );

                        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();
                        ui.checkbox(&mut self.context.grayscale_enabled, "Grayscale");

                        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::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();
                        ui.menu_button(
                            format!("Plugins ({})", self.context.loaded_plugins.len()),
                            |ui| {
                                if self.context.loaded_plugins.is_empty() {
                                    ui.label("No plugins loaded");
                                } else {
                                    for plugin in &self.context.loaded_plugins {
                                        ui.horizontal(|ui| {
                                            ui.label(&plugin.name);
                                            ui.label(
                                                egui::RichText::new(format!("v{}", plugin.version))
                                                    .weak(),
                                            );
                                        });
                                    }
                                }

                                #[cfg(not(target_arch = "wasm32"))]
                                {
                                    ui.separator();
                                    if ui.button("Open Plugins Directory").clicked() {
                                        let plugins_dir = std::env::current_exe()
                                            .ok()
                                            .and_then(|p| p.parent().map(|p| p.to_path_buf()))
                                            .map(|p| p.join("plugins"))
                                            .unwrap_or_else(|| std::path::PathBuf::from("plugins"));

                                        if !plugins_dir.exists() {
                                            let _ = std::fs::create_dir_all(&plugins_dir);
                                        }

                                        #[cfg(target_os = "windows")]
                                        {
                                            let _ = std::process::Command::new("explorer")
                                                .arg(&plugins_dir)
                                                .spawn();
                                        }
                                        #[cfg(target_os = "macos")]
                                        {
                                            let _ = std::process::Command::new("open")
                                                .arg(&plugins_dir)
                                                .spawn();
                                        }
                                        #[cfg(target_os = "linux")]
                                        {
                                            let _ = std::process::Command::new("xdg-open")
                                                .arg(&plugins_dir)
                                                .spawn();
                                        }

                                        ui.close();
                                    }
                                }
                            },
                        );

                        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();
                                    }
                                }
                            },
                        );

                        if self.context.show_left_panel != left_panel_before {
                            publish_app_event(
                                world,
                                EditorEvent::PanelToggled { panel: Panel::Left },
                            );
                        }
                        if self.context.show_right_panel != right_panel_before {
                            publish_app_event(
                                world,
                                EditorEvent::PanelToggled {
                                    panel: Panel::Right,
                                },
                            );
                        }
                    });
                    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 = 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(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("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);
                        }

                        #[cfg(not(target_arch = "wasm32"))]
                        self.render_plugin_menu_items(ui);
                    });
                    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();
                    });
                });
            });
        });
    }

    #[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);
            }
        }
    }

    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 == "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;
        use nightshade::ecs::picking::queries::{PickingOptions, pick_entities};

        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;
        if !mouse.state.contains(MouseState::LEFT_JUST_PRESSED) {
            return;
        }

        if self.context.hover_axis.is_some() {
            return;
        }

        let mouse_pos = mouse.position;
        let all_picked = pick_entities(world, mouse_pos, PickingOptions::default());

        let camera_entity = world.resources.active_camera;
        let gizmo = self.context.gizmo.as_ref();

        let picked_entity = all_picked.iter().find_map(|result| {
            if Some(result.entity) == camera_entity {
                return None;
            }
            if let Some(gizmo) = gizmo
                && gizmos::is_gizmo_entity(world, gizmo, result.entity)
            {
                return None;
            }
            Some(result.entity)
        });

        self.context.selected_entity = picked_entity;
    }

    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;

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

    fn new_project(&mut self, world: &mut World) {
        self.clear_scene(world);
        world.resources.graphics.atmosphere = Atmosphere::CloudySky;
        spawn_sun(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() -> egui_tiles::Tree<Pane> {
        let mut tiles = egui_tiles::Tiles::default();
        let viewport = tiles.insert_pane(Pane::camera(0));
        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::CloudySky;
        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 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,
            );
            if let Some(parent) = world.get_parent_mut(gizmo.root) {
                parent.0 = 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) {
                    if let Some(parent_mut) = world.get_parent_mut(gizmo.root) {
                        parent_mut.0 = 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(parent) = world.get_parent(gizmo.root)
                    && parent.0 != Some(selected_entity)
                {
                    if let Some(parent_mut) = world.get_parent_mut(gizmo.root) {
                        parent_mut.0 = Some(selected_entity);
                    }
                    mark_local_transform_dirty(world, gizmo.root);
                }

                self.set_gizmo_visible(world, gizmo, true);
                gizmos::set_gizmo_drag_visibility(world, gizmo, self.context.drag_mode);
            } else {
                if let Some(parent_mut) = world.get_parent_mut(gizmo.root) {
                    parent_mut.0 = 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);
                }
            }

            if world.resources.user_interface.consumed_event {
                if !matches!(
                    self.context.drag_mode,
                    nightshade::ecs::gizmos::GizmoDragMode::None
                ) {
                    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);
                }
            } 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 {
                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 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 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 = js_sys::Array::new();
        array.push(&wasm_bindgen::JsValue::from_str(&json));

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

        let blob = match 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 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: 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 _ = 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 wasm_bindgen::JsCast;
        use wasm_bindgen::prelude::*;

        let window = match 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: 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: 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 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 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,
            );
        }

        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,
        );
        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(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 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 screen_movement = camera_right * mouse_delta.x - camera_up * mouse_delta.y;
            let movement_amount = nalgebra_glm::dot(&screen_movement, &axis) * sensitivity;
            let world_movement = axis * movement_amount;

            if let Some(transform) = world.get_local_transform(target) {
                let mut new_transform = *transform;
                new_transform.translation += world_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 screen_movement = camera_right * mouse_delta.x * sensitivity
                - camera_up * mouse_delta.y * sensitivity;

            let movement_in_plane =
                screen_movement - plane_normal * nalgebra_glm::dot(&screen_movement, &plane_normal);

            if let Some(transform) = world.get_local_transform(target) {
                let mut new_transform = *transform;
                new_transform.translation += movement_in_plane;
                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 screen_movement = camera_right * mouse_delta.x * sensitivity
                - camera_up * mouse_delta.y * sensitivity;

            if let Some(transform) = world.get_local_transform(target) {
                let mut new_transform = *transform;
                new_transform.translation += screen_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);
            }
        }
    }
}

#[cfg(not(target_arch = "wasm32"))]
impl Editor {
    fn load_plugins(&mut self) {
        let plugins_dir = std::env::current_exe()
            .ok()
            .and_then(|p| p.parent().map(|p| p.to_path_buf()))
            .map(|p| p.join("plugins"))
            .unwrap_or_else(|| std::path::PathBuf::from("plugins"));

        match plugin_runtime::EditorPluginRuntime::new() {
            Ok(mut runtime) => {
                if let Err(error) = runtime.load_plugins_from_directory(&plugins_dir) {
                    tracing::error!("Failed to load plugins: {}", error);
                }
                runtime.call_on_init();
                self.context.loaded_plugins = runtime.get_loaded_plugins();
                self.plugin_runtime = Some(runtime);
            }
            Err(error) => {
                tracing::error!("Failed to create plugin runtime: {}", error);
            }
        }
    }
}
