pub mod inspector;
pub mod inspector_animation;
pub mod inspector_camera;
pub mod inspector_light;
pub mod inspector_lines;
pub mod inspector_material;
pub mod inspector_mesh;
pub mod inspector_name;
pub mod inspector_navmesh;
pub mod inspector_render_layer;
pub mod inspector_script;
pub mod inspector_text;
pub mod inspector_transform;
pub mod inspector_water;
pub mod tree;
pub mod undo;

pub use inspector::*;
pub use tree::*;
pub use undo::*;

use crate::widgets::{Widget, WidgetContext};
use nightshade::ecs::gizmos::{GizmoDragMode, GizmoMode, GizmoState};
use nightshade::ecs::prefab::Prefab;
use nightshade::editor_plugin::ModInfo;
use nightshade::prelude::*;

#[derive(Clone, Copy, PartialEq, Default)]
pub enum TransformOperation {
    #[default]
    None,
    Grab,
    Rotate,
    Scale,
}

#[derive(Clone, Copy, PartialEq, Default)]
pub enum TransformConstraint {
    #[default]
    None,
    X,
    Y,
    Z,
}

pub struct ModalTransformState {
    pub operation: TransformOperation,
    pub constraint: TransformConstraint,
    pub initial_transform: LocalTransform,
    pub start_mouse_pos: Vec2,
    pub accumulated_delta: Vec2,
}

impl Default for ModalTransformState {
    fn default() -> Self {
        Self {
            operation: TransformOperation::None,
            constraint: TransformConstraint::None,
            initial_transform: LocalTransform::default(),
            start_mouse_pos: Vec2::zeros(),
            accumulated_delta: Vec2::zeros(),
        }
    }
}

#[derive(Default)]
pub struct TileBehavior {
    pub viewport_rects: std::collections::HashMap<egui_tiles::TileId, egui::Rect>,
    pub add_widget_to: Option<(egui_tiles::TileId, crate::widgets::WidgetKind)>,
    pub viewport_textures: Vec<egui::TextureId>,
    pub world: Option<*mut World>,
    pub selected_viewport_tile: Option<egui_tiles::TileId>,
    pub prefabs: Vec<PrefabMetadata>,
    pub layout_modified: bool,
}

impl egui_tiles::Behavior<crate::widgets::Pane> for TileBehavior {
    fn tab_title_for_pane(&mut self, pane: &crate::widgets::Pane) -> egui::WidgetText {
        pane.widget.title().into()
    }

    fn pane_ui(
        &mut self,
        ui: &mut egui::Ui,
        tile_id: egui_tiles::TileId,
        pane: &mut crate::widgets::Pane,
    ) -> egui_tiles::UiResponse {
        let rect = ui.available_rect_before_wrap();
        self.viewport_rects.insert(tile_id, rect);

        if let Some(world) = self.world {
            let mut widget_context = WidgetContext {
                world,
                selected_viewport_tile: &mut self.selected_viewport_tile,
                viewport_textures: &self.viewport_textures,
                current_tile_id: tile_id,
            };

            pane.widget.ui(ui, &mut widget_context);
        }

        egui_tiles::UiResponse::None
    }

    fn simplification_options(&self) -> egui_tiles::SimplificationOptions {
        egui_tiles::SimplificationOptions {
            all_panes_must_have_tabs: true,
            ..Default::default()
        }
    }

    fn tab_bar_height(&self, _style: &egui::Style) -> f32 {
        24.0
    }

    fn close_button_outer_size(&self) -> f32 {
        16.0
    }

    fn is_tab_closable(
        &self,
        _tiles: &egui_tiles::Tiles<crate::widgets::Pane>,
        _tile_id: egui_tiles::TileId,
    ) -> bool {
        true
    }

    fn on_tab_close(
        &mut self,
        _tiles: &mut egui_tiles::Tiles<crate::widgets::Pane>,
        _tile_id: egui_tiles::TileId,
    ) -> bool {
        self.layout_modified = true;
        true
    }

    fn on_edit(&mut self, edit_action: egui_tiles::EditAction) {
        match edit_action {
            egui_tiles::EditAction::TileDropped | egui_tiles::EditAction::TileResized => {
                self.layout_modified = true;
            }
            _ => {}
        }
    }

    #[allow(deprecated)]
    fn top_bar_right_ui(
        &mut self,
        tiles: &egui_tiles::Tiles<crate::widgets::Pane>,
        ui: &mut egui::Ui,
        tile_id: egui_tiles::TileId,
        _tabs: &egui_tiles::Tabs,
        _scroll_offset: &mut f32,
    ) {
        let has_materials_widget = tiles.tile_ids().any(|id| {
            matches!(
                tiles.get(id),
                Some(egui_tiles::Tile::Pane(pane))
                    if matches!(pane.widget, crate::widgets::EditorWidget::Materials(_))
            )
        });
        let button_response = ui.button("➕").on_hover_text("Add new widget");

        if button_response.clicked() {
            let popup_id = egui::Id::new("add_widget_popup").with(tile_id);
            ui.memory_mut(|mem| {
                mem.open_popup(popup_id);
                mem.data
                    .get_temp_mut_or_insert_with::<String>(popup_id, String::new);
                mem.data
                    .get_temp_mut_or_insert_with::<usize>(popup_id.with("selected_index"), || 0);
            });
        }

        egui::popup_below_widget(
            ui,
            egui::Id::new("add_widget_popup").with(tile_id),
            &button_response,
            egui::PopupCloseBehavior::CloseOnClickOutside,
            |ui: &mut egui::Ui| {
                ui.set_min_width(200.0);

                let popup_id = egui::Id::new("add_widget_popup").with(tile_id);
                let mut search = ui
                    .memory_mut(|mem| mem.data.get_temp_mut_or_default::<String>(popup_id).clone());

                let popup_just_opened = ui.memory_mut(|mem| {
                    let popup_open_key = popup_id.with("was_just_opened");
                    if mem.is_popup_open(popup_id) {
                        let was_open_before =
                            mem.data.get_temp::<bool>(popup_open_key).unwrap_or(false);
                        if !was_open_before {
                            mem.data.insert_temp(popup_open_key, true);
                            true
                        } else {
                            false
                        }
                    } else {
                        mem.data.insert_temp(popup_open_key, false);
                        false
                    }
                });

                ui.spacing_mut().item_spacing.y = 6.0;

                let search_frame = egui::Frame::NONE
                    .fill(ui.visuals().extreme_bg_color)
                    .inner_margin(egui::Margin::same(8))
                    .stroke(ui.visuals().widgets.noninteractive.bg_stroke);

                search_frame.show(ui, |ui| {
                    ui.horizontal(|ui| {
                        ui.label("🔍");
                        let search_response = ui
                            .text_edit_singleline(&mut search)
                            .on_hover_text("Type to filter widgets");

                        let should_focus = popup_just_opened
                            || ui.memory(|mem| {
                                mem.is_popup_open(popup_id) && !mem.has_focus(search_response.id)
                            });

                        if should_focus {
                            ui.memory_mut(|mem| mem.request_focus(search_response.id));
                            ui.ctx().request_repaint();
                        }

                        if search_response.changed() {
                            ui.memory_mut(|mem| {
                                *mem.data.get_temp_mut_or_default::<String>(popup_id) =
                                    search.clone();
                                *mem.data.get_temp_mut_or_default::<usize>(
                                    popup_id.with("selected_index"),
                                ) = 0;
                            });
                        }

                        if !search.is_empty() && ui.button("✖").clicked() {
                            search.clear();
                            ui.memory_mut(|mem| {
                                *mem.data.get_temp_mut_or_default::<String>(popup_id) =
                                    String::new();
                            });
                        }
                    });
                });

                ui.add_space(4.0);
                ui.separator();

                use strum::IntoEnumIterator;
                let widgets: Vec<crate::widgets::WidgetKind> = crate::widgets::WidgetKind::iter()
                    .filter(|widget| {
                        if has_materials_widget && *widget == crate::widgets::WidgetKind::Materials
                        {
                            return false;
                        }
                        if search.is_empty() {
                            true
                        } else {
                            widget
                                .title()
                                .to_lowercase()
                                .contains(&search.to_lowercase())
                        }
                    })
                    .collect();

                if widgets.is_empty() {
                    ui.vertical_centered(|ui| {
                        ui.add_space(10.0);
                        ui.label("No matches found");
                        ui.add_space(10.0);
                    });
                    return;
                }

                let mut selected_index = ui.memory_mut(|mem| {
                    *mem.data
                        .get_temp_mut_or_default::<usize>(popup_id.with("selected_index"))
                });

                if selected_index >= widgets.len() {
                    selected_index = widgets.len() - 1;
                    ui.memory_mut(|mem| {
                        *mem.data
                            .get_temp_mut_or_default::<usize>(popup_id.with("selected_index")) =
                            selected_index
                    });
                }

                if ui.input_mut(|i| i.consume_key(egui::Modifiers::NONE, egui::Key::ArrowDown))
                    && selected_index < widgets.len() - 1
                {
                    selected_index += 1;
                    ui.memory_mut(|mem| {
                        *mem.data
                            .get_temp_mut_or_default::<usize>(popup_id.with("selected_index")) =
                            selected_index
                    });
                }

                if ui.input_mut(|i| i.consume_key(egui::Modifiers::NONE, egui::Key::ArrowUp))
                    && selected_index > 0
                {
                    selected_index -= 1;
                    ui.memory_mut(|mem| {
                        *mem.data
                            .get_temp_mut_or_default::<usize>(popup_id.with("selected_index")) =
                            selected_index
                    });
                }

                let enter_pressed =
                    ui.input_mut(|i| i.consume_key(egui::Modifiers::NONE, egui::Key::Enter));

                let max_items = 8;
                let item_height = 28.0;
                let max_height = (widgets.len().min(max_items) as f32 * item_height) + 4.0;

                egui::ScrollArea::vertical()
                    .id_salt(ui.next_auto_id())
                    .max_height(max_height)
                    .auto_shrink([false, false])
                    .show_viewport(ui, |ui, _viewport| {
                        ui.style_mut().visuals.button_frame = false;
                        ui.style_mut().spacing.item_spacing.y = 1.0;

                        if enter_pressed
                            || ui.input(|i| {
                                i.key_pressed(egui::Key::ArrowUp)
                                    || i.key_pressed(egui::Key::ArrowDown)
                            })
                        {
                            let selected_pos = selected_index as f32 * item_height;
                            ui.scroll_to_rect(
                                egui::Rect::from_min_size(
                                    egui::pos2(0.0, selected_pos),
                                    egui::vec2(1.0, item_height),
                                ),
                                None,
                            );
                        }

                        for (index, widget_kind) in widgets.iter().enumerate() {
                            let is_selected = index == selected_index;
                            let display_name = widget_kind.title();

                            let button = egui::Button::new(
                                egui::RichText::new(display_name).size(14.0).color(
                                    if is_selected {
                                        ui.visuals().selection.stroke.color
                                    } else {
                                        ui.visuals().text_color()
                                    },
                                ),
                            );

                            let button_response = ui.add_sized(
                                [ui.available_width(), item_height],
                                button.fill(if is_selected {
                                    ui.visuals().selection.bg_fill
                                } else {
                                    ui.visuals().extreme_bg_color
                                }),
                            );

                            if button_response.hovered() && !is_selected {
                                ui.memory_mut(|mem| {
                                    *mem.data.get_temp_mut_or_default::<usize>(
                                        popup_id.with("selected_index"),
                                    ) = index
                                });
                            }

                            if button_response.clicked() || (enter_pressed && is_selected) {
                                self.add_widget_to = Some((tile_id, *widget_kind));
                                self.layout_modified = true;
                                ui.memory_mut(|mem| {
                                    *mem.data.get_temp_mut_or_default::<usize>(
                                        popup_id.with("selected_index"),
                                    ) = 0;
                                });
                                ui.ctx().request_repaint();
                            }
                        }
                    });

                ui.separator();
                ui.horizontal(|ui| {
                    ui.spacing_mut().item_spacing.x = 4.0;
                    ui.visuals_mut().widgets.noninteractive.fg_stroke.color =
                        ui.visuals().weak_text_color();
                    ui.label("Up/Down to navigate");
                    ui.add_space(8.0);
                    ui.label("Enter to select");
                });
            },
        );
    }
}

pub struct PrefabMetadata {
    pub prefab: Prefab,
    pub animations: Vec<nightshade::ecs::animation::components::AnimationClip>,
    pub source_path: Option<String>,
}

pub struct EditorContext {
    pub show_left_panel: bool,
    pub show_right_panel: bool,
    pub show_assets_window: bool,
    pub show_animations_window: bool,
    pub selected_entity: Option<Entity>,
    pub grayscale_enabled: bool,
    pub hovered_file_path: Option<std::path::PathBuf>,
    pub drag_file_type: Option<String>,
    pub gizmo: Option<GizmoState>,
    pub gizmo_mode: GizmoMode,
    pub hover_axis: Option<Vec3>,
    pub drag_mode: GizmoDragMode,
    pub camera_distance: f32,
    pub tile_tree: Option<egui_tiles::Tree<crate::widgets::Pane>>,
    pub tile_behavior: TileBehavior,
    pub current_project_path: Option<std::path::PathBuf>,
    pub current_project_name: Option<String>,
    pub hdr_skybox_path: Option<std::path::PathBuf>,
    pub loaded_plugins: Vec<ModInfo>,
    pub pending_asset_update: Option<(String, String)>,
    pub editing_project_name: bool,
    pub project_name_edit_buffer: String,
    pub editing_layout_index: Option<usize>,
    pub layout_name_edit_buffer: String,
    pub layout_is_modified: bool,
    pub pending_material_edit: Option<String>,
    pub pending_info_toast: Option<String>,
    pub scripts_running: bool,
    pub play_mode: bool,
    pub undo_history: UndoHistory,
    pub modal_transform: ModalTransformState,
    pub add_primitive_popup: Option<Vec2>,
    pub entity_context_menu: Option<Vec2>,
    #[cfg(target_arch = "wasm32")]
    pub pending_project_load: std::rc::Rc<std::cell::RefCell<Option<Vec<u8>>>>,
}

impl Default for EditorContext {
    fn default() -> Self {
        Self {
            show_left_panel: false,
            show_right_panel: false,
            show_assets_window: false,
            show_animations_window: false,
            selected_entity: None,
            grayscale_enabled: false,
            hovered_file_path: None,
            drag_file_type: None,
            gizmo: None,
            gizmo_mode: GizmoMode::GlobalTranslation,
            hover_axis: None,
            drag_mode: GizmoDragMode::None,
            camera_distance: 10.0,
            tile_tree: None,
            tile_behavior: TileBehavior::default(),
            current_project_path: None,
            current_project_name: None,
            hdr_skybox_path: None,
            loaded_plugins: Vec::new(),
            pending_asset_update: None,
            editing_project_name: false,
            project_name_edit_buffer: String::new(),
            editing_layout_index: None,
            layout_name_edit_buffer: String::new(),
            layout_is_modified: false,
            pending_material_edit: None,
            pending_info_toast: None,
            scripts_running: false,
            play_mode: false,
            undo_history: UndoHistory::default(),
            modal_transform: ModalTransformState::default(),
            add_primitive_popup: None,
            entity_context_menu: None,
            #[cfg(target_arch = "wasm32")]
            pending_project_load: std::rc::Rc::new(std::cell::RefCell::new(None)),
        }
    }
}
