pub mod camera;
pub mod render;

mod tacmap {
    pub use super::render;
    pub use super::camera;
}

use std::cell::RefCell;
use std::time::Duration;

use winit::event::ElementState;
use winit::keyboard::KeyCode;

use crate::GameState;
use crate::canvas::Canvas;
use crate::solar_system::SolarSystem;
use crate::solar_system::SystemId;
use crate::wgpuctx::WgpuCtx;
use render::*;
use camera::*;
use crate::window::ui::GameWindowUiState;

pub struct TacticalMap
{
    canvas: Canvas,
    camera: Camera,
    pmatrix: Projection,
 
    camera_controller: CameraController,
    renderstate: Option<(SystemId, BodyRenderer)>
}

impl TacticalMap
{
    pub fn new(
        wgpuctx: &WgpuCtx,
        position: winit::dpi::LogicalPosition<f32>,
        size: winit::dpi::LogicalSize<f32>)
    -> Self {
        let surface_size = winit::dpi::PhysicalSize::new(
            wgpuctx.surface_config().width,
            wgpuctx.surface_config().height
        );

        let canvas = Canvas::new(
            wgpuctx,
            surface_size,
            position,
            size).unwrap();
    
        let camera = Camera::new(
            wgpuctx,
            cgmath::Point3::<f32>::new(0.0, 0.0, 1.0),
            cgmath::Deg(-90.0),
            cgmath::Rad(0.0));

        let projection = Projection::new(
            surface_size.width,
            surface_size.height,
            cgmath::Deg(60.0),
            (0.1, 1000.0));

        Self {
            canvas: canvas,
            camera: camera,
            pmatrix: projection,
            camera_controller: CameraController::new(),
            renderstate: None,
        }
    }

    pub fn resize(
        &mut self,
        wgpuctx: &WgpuCtx,
        width: u32,
        height: u32)
    {
        self.canvas.resize(wgpuctx, width, height);
        self.pmatrix.resize(width, height);
    }

    pub fn update(
        &mut self,
        game_state: &RefCell<GameState>,
        ui_state: &mut GameWindowUiState,
        dt: Duration)
    {
        self.camera_controller.update(&mut self.camera, dt);
        ui_state.camera_scale = self.camera.get_scale();
    }

    pub fn keyboard_input(
        &mut self,
        game_state: &RefCell<GameState>,
        key_code: KeyCode,
        key_state: ElementState)
    {
        self.camera_controller.keyboard_input(key_code, key_state);
    }

    pub fn draw(
        &mut self,
        wgpuctx: &WgpuCtx,
        game_state: &RefCell<GameState>,
        current_system: Option<SystemId>)
    -> Result<(), wgpu::SurfaceError>
    {
        let game_state = game_state.borrow();

        let current_system_id = match current_system {
            Some(system) => system,
            None => return Ok(())
        };
        let solar_systems = game_state.solar_systems();
        let current_system = &solar_systems[current_system_id];

        self.camera.update_buffer(wgpuctx, &self.pmatrix);

        let tacrender = match &mut self.renderstate {
            Some((id, render)) => {
                if *id != current_system_id {
                    *id = current_system_id;
                    render.mark_to_rebuild();
                }
                render
            },
            None => {
                let tmp = render::BodyRenderer::new(wgpuctx);
                self.renderstate = Some((current_system_id, tmp));
                &mut self.renderstate.as_mut().unwrap().1
            }
        };

        //Update buffers for the current system and time.
        tacrender.rebuild(wgpuctx, current_system);
        match tacrender.update(
            wgpuctx, current_system, 
            game_state.timeman().seconds())
        {
            Ok(()) => {},
            Err(e) => println!("Tactical map render update error: {}", e)
        }

        tacrender.render(wgpuctx, &self.canvas, &self.camera);

        Ok(())
    }

    pub fn present(
        &mut self,
        screen_pass: &mut wgpu::RenderPass)
    -> Result<(), wgpu::SurfaceError>
    {
        self.canvas.present(screen_pass)?;
        Ok(())
    }
} // impl SystemViewport
