//! Game project.
use fyrox::graph::SceneGraph;
use fyrox::{
    asset::manager::ResourceManager,
    core::{log::Log, pool::Handle},
    engine::GraphicsContext,
    event::{Event, WindowEvent},
    gui::{
        button::ButtonMessage,
        message::{MessageDirection, UiMessage},
        widget::WidgetMessage,
        UiNode, UserInterface,
    },
    plugin::{Plugin, PluginConstructor, PluginContext, PluginRegistrationContext},
    scene::{graph::Graph, Scene},
};
use std::path::Path;
mod enemies;
mod map;
mod msg;
mod player;

use enemies::swordsman::{enemy::Swordman, spawn::SwordmanSpawn};
use enemies::archer::{enemy::Archer, spawn::ArcherSpawn};
use map::build_map;
use msg::Message;
use player::Player;

pub struct GameConstructor;

impl PluginConstructor for GameConstructor {
    fn register(&self, context: PluginRegistrationContext) {
        // Register your scripts here.
        let script_constructors = &context.serialization_context.script_constructors;
        script_constructors.add::<Swordman>("Enemy");
        script_constructors.add::<Player>("Player");
    }

    fn create_instance(&self, scene_path: Option<&str>, context: PluginContext) -> Box<dyn Plugin> {
        Box::new(Game::new(scene_path, context))
    }
}

pub struct Game {
    scene: Handle<Scene>,

    new_game: Handle<UiNode>,
    exit: Handle<UiNode>,
    level: u32,
}

impl Game {
    pub fn new(_scene_path: Option<&str>, ctx: PluginContext) -> Self {
        ctx.task_pool.spawn_plugin_task(
            UserInterface::load_from_file("data/menu.ui", ctx.resource_manager.clone()),
            |result, game: &mut Game, ctx| match result {
                Ok(menu) => {
                    *ctx.user_interface = menu;
                    (game.new_game, _) = ctx
                        .user_interface
                        .find_by_name_from_root("NewGame")
                        .unwrap();
                    (game.exit, _) = ctx.user_interface.find_by_name_from_root("Exit").unwrap();
                }
                Err(e) => Log::err(format!("Unable to load main menu! Reason: {:?}", e)),
                // let ui = ctx.user_interface.clone();
                // *ui = result.unwrap();
            },
        );
        Self {
            scene: Handle::NONE,
            new_game: Handle::NONE,
            exit: Handle::NONE,
            level: 1,
        }
    }
}

impl Plugin for Game {
    fn on_deinit(&mut self, _context: PluginContext) {
        // Do a cleanup here.
    }

    fn update(&mut self, _context: &mut PluginContext) {
        // Add your global update code here.
    }

    fn on_os_event(&mut self, event: &Event<()>, context: PluginContext) {
        if let Event::Resumed {} = event {
            if let GraphicsContext::Initialized(ref graphics_context) = context.graphics_context {
                let window_size = graphics_context.window.inner_size();
                for script_scene in &context.script_processor.scripted_scenes {
                    script_scene.message_sender.send_global(Message::Resize {
                        width: window_size.width,
                        height: window_size.height,
                    });
                }
            }
        }
        if let Event::WindowEvent { event, .. } = event {
            if let WindowEvent::Resized(size) = event {
                for script_scene in &context.script_processor.scripted_scenes {
                    script_scene.message_sender.send_global(Message::Resize {
                        width: size.width,
                        height: size.height,
                    });
                }
            }
        }
    }

    fn on_ui_message(&mut self, ctx: &mut PluginContext, message: &UiMessage) {
        if let Some(ButtonMessage::Click) = message.data() {
            if message.destination() == self.new_game {
                ctx.user_interface.send_message(WidgetMessage::visibility(
                    ctx.user_interface.root(),
                    MessageDirection::ToWidget,
                    false,
                ));
                ctx.async_scene_loader.request("data/scene.rgs");
            } else if message.destination() == self.exit {
                if let Some(window_target) = ctx.window_target {
                    window_target.exit();
                }
            }
        }
    }

    fn on_scene_begin_loading(&mut self, _path: &Path, ctx: &mut PluginContext) {
        if self.scene.is_some() {
            ctx.scenes.remove(self.scene);
        }
    }

    fn on_scene_loaded(
        &mut self,
        _path: &Path,
        scene: Handle<Scene>,
        _data: &[u8],
        context: &mut PluginContext,
    ) {
        context
            .user_interface
            .send_message(WidgetMessage::visibility(
                context.user_interface.root(),
                MessageDirection::ToWidget,
                false,
            ));
        self.scene = scene;

        let graph: &mut Graph = &mut context.scenes[self.scene].graph;
        let resource_manager: &ResourceManager = &context.resource_manager;
        build_map(graph, resource_manager);
        if self.level == 1 {
            SwordmanSpawn::new().spawn_enemy(graph, resource_manager, -5.0);
        }
        if self.level == 2 {
            ArcherSpawn::new().spawn_enemy(graph, resource_manager, -5.0);
        }
    }
}
