mod window;
mod ui;
mod tacmap;
mod wgpuctx;
mod eguictx;
mod vertex;
mod texture;
mod solar_system;
mod fleet;
mod known_stars;
mod timeman;
mod ntree;

use std::cell::RefCell;
use std::thread;
use std::time::{Duration, Instant};

use winit::event::{KeyEvent, WindowEvent};
use winit::event_loop::{EventLoop, ActiveEventLoop, ControlFlow};
use winit::error::{EventLoopError};
use winit::application::{ApplicationHandler};
use winit::keyboard::PhysicalKey;
use winit::window::WindowId;

use solar_system::*;

use crate::fleet::FleetsManager;
use crate::fleet::schedule::ScheduleManager;
use crate::timeman::TimeMan;
use crate::window::GameWindow;

const TARGET_FPS: f32 = 100.0;
const TARGET_DT: Duration = Duration::from_millis((1000.0 / TARGET_FPS) as u64);

struct SystemicApp
{
    window: Option<GameWindow>,
    wgpu_instance: wgpu::Instance,

    last_render_time: Instant,
    game_state: Option<RefCell<GameState>>
}

struct GameState
{
    timeman: TimeMan,
    solar_systems: Vec<SolarSystem>,
    fleets: FleetsManager,
    scheduler: ScheduleManager,
}

impl SystemicApp
{
    fn create_window(
        &mut self,
        event_loop: &ActiveEventLoop)
    {
        self.window = Some(GameWindow::new(
                &self.wgpu_instance,
                event_loop).unwrap());

        self.game_state = Some(RefCell::new(GameState::new()));
    }
}

impl GameState
{
    pub fn new()
    -> Self {
        let timeman = TimeMan::new(0);
        let sol_system = match SolarSystem::new_from_known_star(0, "sol")
        {
            Ok(system) => system,
            Err(e) => panic!("Unable to create sol system : {}", e)
        };

        Self {
            timeman: timeman,
            solar_systems: vec![ sol_system ],
            fleets: FleetsManager::new(),
            scheduler: ScheduleManager::new()
        }
    }

    pub fn solar_systems(&self) -> &[SolarSystem]
    { self.solar_systems.as_slice() }

    pub fn solar_systems_mut(&mut self) -> &mut [SolarSystem]
    { self.solar_systems.as_mut_slice() }

    pub fn fleets(&self) -> &FleetsManager
    { &self.fleets }

    pub fn fleets_mut(&mut self) -> &mut FleetsManager
    { &mut self.fleets }

    pub fn timeman(&self) -> &TimeMan
    { &self.timeman }

    pub fn timeman_mut(&mut self) -> &mut TimeMan
    { &mut self.timeman }

    fn update(
        &mut self,
        _dt: Duration)
    {
        let time_then = self.timeman.update();
        if time_then == self.timeman.seconds() { return; }

        self.fleets.tick(
            &self.solar_systems,
            &mut self.scheduler,
            time_then..self.timeman.seconds());
    }
}

impl ApplicationHandler for SystemicApp
{
    fn resumed(
        &mut self, 
        event_loop: &ActiveEventLoop
    ) {
        match self.window {
            Some(_) => {}
            None => self.create_window(event_loop)
        }
    }

    fn window_event(
            &mut self,
            event_loop: &ActiveEventLoop,
            _window_id: WindowId,
            event: winit::event::WindowEvent,
    ) {
        let window = match &mut self.window {
            Some(w) => w,
            None => return
        };

        let game_state = match &self.game_state {
            Some(state) => state,
            None => return
        };

        let event_resp = window.on_event(&event);

        match event {
            WindowEvent::CloseRequested => {
                event_loop.exit();  
            }
            WindowEvent::Resized(size) => {
                window.resize(size.width, size.height);
            }
            WindowEvent::RedrawRequested => {
                let now = Instant::now();
                let delta_time = now - self.last_render_time;
                self.last_render_time = now;

                game_state.borrow_mut().update(delta_time);
                window.update(game_state, delta_time);
                
                match window.render(game_state, event_resp, delta_time) {
                    Ok(_) => {}
                    Err(wgpu::CurrentSurfaceTexture::Suboptimal(_) |
                        wgpu::CurrentSurfaceTexture::Outdated |
                        wgpu::CurrentSurfaceTexture::Lost
                    ) => {
                        let size = window.size();
                        window.resize(size.width, size.height);
                    },
                    Err(_) => {
                        log::error!("Uncaught error while trying to render!");
                    }
                }
                if delta_time < TARGET_DT {
                    thread::sleep(TARGET_DT - delta_time);
                }
            }
            WindowEvent::KeyboardInput {
                event: KeyEvent {
                    physical_key: PhysicalKey::Code(key_code),
                    state: key_state,
                    ..
                },
                ..
            } => {
                if !event_resp.consumed {
                    window.keyboard_input(key_code, key_state);
                }
            }
            _ => {}
        }
    }
} // impl ApplicationHandler for SystemicApp

fn main()
    -> Result<(), EventLoopError>
{
    env_logger::init();
    let event_loop = EventLoop::new().unwrap();

    event_loop.set_control_flow(ControlFlow::Poll);

    let mut app = SystemicApp {
        window: None,
        wgpu_instance: wgpu::Instance::default(),
        last_render_time: Instant::now(),
        game_state: None
    };

    event_loop.run_app(&mut app)
}
