use crate::mosaic::behavior::{TileBehavior, TileBehaviorWithWorld};
use crate::mosaic::widget::{Pane, Widget, WidgetContext};
use nightshade::prelude::*;

use super::Mosaic;

impl<W: Widget<C, M>, C, M> Mosaic<W, C, M> {
    pub fn show_inside(&mut self, world: &mut World, ui: &mut egui::Ui, app: &mut C) {
        self.prepare(world);
        let mut tree = self.tile_tree.take();

        Self::render_tree(
            &mut tree,
            &mut self.behavior,
            world,
            app,
            &mut self.pending_messages,
            ui,
        );

        self.process_pending_removals(world, app);
        self.finish(tree);
    }

    fn render_tree(
        tree: &mut Option<egui_tiles::Tree<Pane<W>>>,
        behavior: &mut TileBehavior<W, C, M>,
        world: &mut World,
        app: &mut C,
        pending: &mut Vec<M>,
        ui: &mut egui::Ui,
    ) {
        if let Some(tree) = tree {
            {
                let mut behavior_with_world = TileBehaviorWithWorld {
                    behavior: &mut *behavior,
                    world: &mut *world,
                    app: &mut *app,
                    messages: &mut *pending,
                };
                tree.ui(&mut behavior_with_world, ui);
            }
            Self::process_pending_add(behavior, tree, app, world, pending);
        }
    }

    pub(super) fn prepare(&mut self, world: &mut World) {
        self.behavior.viewport_rects.clear();

        self.behavior.viewport_textures.clear();
        let source_textures = self
            .viewport_texture_overrides
            .as_ref()
            .unwrap_or(&world.resources.user_interface.viewport_textures);
        self.behavior
            .viewport_textures
            .extend_from_slice(source_textures);

        self.behavior.config = self.config.clone();
        self.behavior.is_active_window = self.is_active_window;

        self.behavior.cached_cameras.clear();
        self.behavior
            .cached_cameras
            .extend(world.core.query_entities(CAMERA));
        self.behavior.cached_cameras.sort_by_key(|entity| entity.id);

        if let Some(tree) = &self.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(&self.behavior.cached_cameras)
                    && !world
                        .resources
                        .user_interface
                        .required_cameras
                        .contains(&camera_entity)
                {
                    world
                        .resources
                        .user_interface
                        .required_cameras
                        .push(camera_entity);
                    world
                        .resources
                        .user_interface
                        .required_camera_sizes
                        .push((0, 0));
                }
            }
        }
    }

    pub(super) fn finish(&mut self, tree: Option<egui_tiles::Tree<Pane<W>>>) {
        self.tile_tree = tree;
        self.incoming_messages.clear();

        if self.behavior.layout_modified {
            self.layout_modified = true;
            self.behavior.layout_modified = false;
        }
    }

    fn process_pending_add(
        behavior: &mut TileBehavior<W, C, M>,
        tree: &mut egui_tiles::Tree<Pane<W>>,
        app: &mut C,
        world: &mut World,
        messages: &mut Vec<M>,
    ) {
        if let Some((tile_id, catalog_index)) = behavior.pending_add_widget.take() {
            let catalog = W::catalog();
            if let Some(entry) = catalog.get(catalog_index) {
                let mut new_widget = (entry.create)();
                let new_pane = Pane::new(new_widget.clone());
                let new_tile_id = tree.tiles.insert_pane(new_pane);
                Self::add_to_container(&mut tree.tiles, tile_id, new_tile_id);

                let mut context = WidgetContext {
                    world,
                    selected_viewport_tile: &mut behavior.selected_viewport_tile,
                    viewport_textures: &behavior.viewport_textures,
                    current_tile_id: new_tile_id,
                    app,

                    cached_cameras: &behavior.cached_cameras,
                    messages,
                };
                new_widget.on_add(&mut context);

                if let Some(egui_tiles::Tile::Pane(pane)) = tree.tiles.get_mut(new_tile_id) {
                    pane.widget = new_widget;
                }
            }
        }
    }

    fn process_pending_removals(&mut self, world: &mut World, app: &mut C) {
        let removals = std::mem::take(&mut self.behavior.pending_removals);
        for (tile_id, mut widget) in removals {
            let mut context = WidgetContext {
                world: &mut *world,
                selected_viewport_tile: &mut self.behavior.selected_viewport_tile,
                viewport_textures: &self.behavior.viewport_textures,
                current_tile_id: tile_id,
                app: &mut *app,

                cached_cameras: &self.behavior.cached_cameras,
                messages: &mut self.pending_messages,
            };
            widget.on_remove(&mut context);
        }
    }
}
