mod management;
#[cfg(not(target_arch = "wasm32"))]
mod persistence;
mod rendering;

#[cfg(not(target_arch = "wasm32"))]
pub use persistence::format_window_title;

use super::behavior::TileBehavior;
use super::config::MosaicConfig;
use super::project::WindowLayout;
use super::widget::{Pane, Widget};
use nightshade::prelude::*;

#[cfg(not(target_arch = "wasm32"))]
pub enum LayoutEvent {
    Switched(String),
    Created(String),
    Saved(String),
    Deleted(String),
    Reset,
    Renamed(String),
}

pub struct Mosaic<W: Widget<C, M>, C = (), M = ()> {
    tile_tree: Option<egui_tiles::Tree<Pane<W>>>,
    pub config: MosaicConfig,
    pub(crate) behavior: TileBehavior<W, C, M>,
    layout_modified: bool,
    viewport_texture_overrides: Option<Vec<egui::TextureId>>,
    pub is_active_window: bool,
    pending_messages: Vec<M>,
    incoming_messages: std::collections::HashMap<egui_tiles::TileId, Vec<M>>,
    layouts: Vec<WindowLayout<W>>,
    active_layout_index: usize,
    editing_layout_index: Option<usize>,
    layout_name_edit_buffer: String,
}

impl<W: Widget<C, M>, C, M> Default for Mosaic<W, C, M> {
    fn default() -> Self {
        Self {
            tile_tree: None,
            config: MosaicConfig::default(),
            behavior: TileBehavior::default(),
            layout_modified: false,
            viewport_texture_overrides: None,
            is_active_window: true,
            pending_messages: Vec::new(),
            incoming_messages: std::collections::HashMap::new(),
            layouts: Vec::new(),
            active_layout_index: 0,
            editing_layout_index: None,
            layout_name_edit_buffer: String::new(),
        }
    }
}

impl<W: Widget<C, M>, C, M> Mosaic<W, C, M> {
    pub fn with_tree(tree: egui_tiles::Tree<Pane<W>>) -> Self {
        Self {
            tile_tree: Some(tree),
            ..Self::default()
        }
    }

    pub fn set_tree(&mut self, tree: egui_tiles::Tree<Pane<W>>) {
        self.tile_tree = Some(tree);
        self.layout_modified = true;
    }

    pub fn current_tree(&self) -> Option<&egui_tiles::Tree<Pane<W>>> {
        self.tile_tree.as_ref()
    }

    pub fn take_layout_modified(&mut self) -> bool {
        let modified = self.layout_modified;
        self.layout_modified = false;
        modified
    }

    pub fn viewport_rects(&self) -> &std::collections::HashMap<egui_tiles::TileId, egui::Rect> {
        &self.behavior.viewport_rects
    }

    pub fn clear_required_cameras(world: &mut World) {
        world.resources.user_interface.required_cameras.clear();
        world.resources.user_interface.required_camera_sizes.clear();
    }

    pub fn find_widget(&self, predicate: impl Fn(&W) -> bool) -> Option<egui_tiles::TileId> {
        if let Some(tree) = &self.tile_tree {
            for (tile_id, tile) in tree.tiles.iter() {
                if let egui_tiles::Tile::Pane(pane) = tile
                    && predicate(&pane.widget)
                {
                    return Some(*tile_id);
                }
            }
        }
        None
    }

    pub fn get_widget_mut(&mut self, tile_id: egui_tiles::TileId) -> Option<&mut W> {
        if let Some(tree) = &mut self.tile_tree
            && let Some(egui_tiles::Tile::Pane(pane)) = tree.tiles.get_mut(tile_id)
        {
            return Some(&mut pane.widget);
        }
        None
    }

    pub fn insert_pane(&mut self, widget: W) -> Option<egui_tiles::TileId> {
        if let Some(tree) = &mut self.tile_tree {
            let new_tile_id = tree.tiles.insert_pane(Pane::new(widget));
            if let Some(root) = tree.root {
                Self::add_to_container(&mut tree.tiles, root, new_tile_id);
            }
            self.layout_modified = true;
            Some(new_tile_id)
        } else {
            let mut tiles = egui_tiles::Tiles::default();
            let pane_id = tiles.insert_pane(Pane::new(widget));
            let root = tiles.insert_tab_tile(vec![pane_id]);
            self.tile_tree = Some(egui_tiles::Tree::new("mosaic_tree", root, tiles));
            self.layout_modified = true;
            Some(pane_id)
        }
    }

    pub(crate) fn add_to_container(
        tiles: &mut egui_tiles::Tiles<Pane<W>>,
        container_id: egui_tiles::TileId,
        child_id: egui_tiles::TileId,
    ) {
        match tiles.get_mut(container_id) {
            Some(egui_tiles::Tile::Container(egui_tiles::Container::Tabs(tabs))) => {
                tabs.add_child(child_id);
                tabs.set_active(child_id);
            }
            Some(egui_tiles::Tile::Container(egui_tiles::Container::Linear(linear))) => {
                linear.add_child(child_id);
            }
            Some(egui_tiles::Tile::Container(egui_tiles::Container::Grid(grid))) => {
                grid.add_child(child_id);
            }
            _ => {}
        }
    }

    pub fn drain_messages(&mut self) -> Vec<M> {
        self.pending_messages.drain(..).collect()
    }
}
