//! Game project.
use fyrox::{
    monitor::MonitorHandle,
    keyboard::PhysicalKey,
    asset::manager::ResourceManager,
    core::{pool::Handle, uuid::Uuid, TypeUuidProvider, log::Log},
    event::{ElementState, Event, WindowEvent, TouchPhase},
    keyboard::KeyCode,
    gui::message::UiMessage,
    impl_component_provider,
    plugin::{Plugin, PluginConstructor, PluginContext, PluginRegistrationContext},
    scene::{
        Scene,
        graph::Graph,
    },
    script::{ScriptContext, ScriptTrait},
};
use std::path::Path;
mod map;
mod player;

use map::build_map;
use player::Player;

pub struct GameConstructor;

impl_component_provider!(Player,);

impl ScriptTrait for Player {
    // Called once at initialization.
    fn on_init(&mut self, context: &mut ScriptContext) {
        self.init(context);
        self.change_orientation(context);
        self.window_height = 600;
        self.window_height = 800;
    }

    // Put start logic - it is called when every other script is already initialized.
    fn on_start(&mut self, _context: &mut ScriptContext) {}

    // Called whenever there is an event from OS (mouse click, keypress, etc.)
    fn on_os_event(&mut self, event: &Event<()>, _context: &mut ScriptContext) {
        if let Event::WindowEvent { event, .. } = event {
            if let WindowEvent::KeyboardInput { event, .. } = event {
                if let PhysicalKey::Code(keycode) = event.physical_key {
                    let is_pressed = event.state == ElementState::Pressed;
                    self.key_checker(keycode, is_pressed);
                }
            }
            if let WindowEvent::Touch(touch) = event {
                match touch.phase {
                    TouchPhase::Started => {
                        Log::info(format!("touch x {:?}, y {:?}", touch.location.x, touch.location.y));
                        if touch.location.x > (self.window_width / 2) as f64 {
                            self.key_checker(KeyCode::ArrowRight, true);
                            self.key_checker(KeyCode::ArrowLeft, false);
                        } else {
                            self.key_checker(KeyCode::ArrowLeft, true);
                            self.key_checker(KeyCode::ArrowRight, false);
                        }
                        if touch.location.y < (self.window_height / 2) as f64 {
                            self.key_checker(KeyCode::ArrowUp, true);
                        }
                        if touch.location.y > (self.window_height / 2) as f64 {
                            self.key_checker(KeyCode::ArrowUp, false);
                        }
                    },
                    TouchPhase::Ended => {
                        if touch.location.x > (self.window_width / 2) as f64 {
                            self.key_checker(KeyCode::ArrowRight, false);
                        } else {
                            self.key_checker(KeyCode::ArrowLeft, false);
                        }
                        if touch.location.y < (self.window_height / 2) as f64 {
                            self.key_checker(KeyCode::ArrowUp, false);
                        }
                        
                    },
                    _ => {},
                }
            }

            if let WindowEvent::Resized(size) = event {
                Log::info(format!("Resized xheight{:?}, width {:?}", size.height, size.width));
                self.window_height = size.height;
                self.window_width = size.width;
            }
        }
    }

    // Called every frame at fixed rate of 60 FPS.
    fn on_update(&mut self, context: &mut ScriptContext) {
        // The script can be assigned to any scene node, but we assert that it will work only with
        // 2d rigid body nodes.
        self.jump_do(context);
        self.move_do(context);
        self.change_orientation(context);
        self.animation_do(context);
        
        self.loop_over(context);
    }

    // Returns unique script ID for serialization needs.
    fn id(&self) -> Uuid {
        Self::type_uuid()
    }
}

impl PluginConstructor for GameConstructor {
    fn register(&self, context: PluginRegistrationContext) {
        // Register your scripts here.
        let script_constructors = &context.serialization_context.script_constructors;
        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>,
}

impl Game {
    pub fn new(scene_path: Option<&str>, context: PluginContext) -> Self {
        context
            .async_scene_loader
            .request(scene_path.unwrap_or("data/scene.rgs"));

        Self {
            scene: Handle::NONE,
        }
    }
}

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) {
        // Do something on OS event here.
    }

    fn on_ui_message(&mut self, _context: &mut PluginContext, _message: &UiMessage) {
        // Handle UI events here.
    }

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