use std::cell::RefCell;
use std::sync::{Arc};
use std::time::Duration;

use winit::event::{ElementState, WindowEvent};
use winit::keyboard::KeyCode;

use crate::tacmap::TacticalMap;
use crate::ui::contact::MapContact;
use crate::{GameState, ui};
use crate::wgpuctx::{RenderPassBuilder, SceneCtx, WgpuCtx};
use crate::eguictx::EguiCtx;

pub struct GameWindow
{
    wgpuctx: WgpuCtx,
    eguictx: EguiCtx,

    window: Arc<winit::window::Window>,

    tactical_map: TacticalMap,

    ui_state: ui::State
}

impl GameWindow
{
    pub fn new(
        instance: &wgpu::Instance,
        event_loop: &winit::event_loop::ActiveEventLoop)
    -> Result<GameWindow, ()>
    {
        let window_attrs = winit::window::Window::default_attributes()
            .with_title("Systemic 4X")
            .with_inner_size(winit::dpi::LogicalSize::new(640, 480));

        let window = Arc::new(event_loop.create_window(window_attrs).unwrap());

        let wgpuctx = pollster::block_on(WgpuCtx::new(instance, window.clone()));
        let eguictx = EguiCtx::new(&window, &wgpuctx);
        ui::set_theme(eguictx.context());

        let tacmap = TacticalMap::new(&wgpuctx);

        let ui_state = ui::State{
            current_system: Some(0),
            camera_target: Some(MapContact::from_body(0)),
            auto_tick: (false, 1),
            manual_tick: 1,
            ..Default::default()
        };

        Ok(Self {
        window: window,
            wgpuctx: wgpuctx,
            eguictx: eguictx,
            tactical_map: tacmap,
            
            ui_state: ui_state
        })
    }
    
    pub fn update(
        &mut self,
        game_state: &RefCell<GameState>,
        dt: Duration)
    {
        let mut game_state = game_state.borrow_mut();
    
        game_state.timeman.auto_tick = match self.ui_state.auto_tick.0 {
            true => Some(self.ui_state.auto_tick.1),
            false => None
        };

        let current_system = match self.ui_state.current_system {
            Some(id) => &game_state.solar_systems()[id],
            None => { return; }
        };

        self.tactical_map.update(
            current_system,
            game_state.fleets(),
            &mut self.ui_state,
            game_state.timeman().seconds(),
            dt);
    }

    pub fn keyboard_input(
        &mut self,
        key_code: KeyCode,
        key_state: ElementState)
    {
        self.tactical_map.keyboard_input(key_code, key_state);
    }

    pub fn render(
        &mut self,
        game_state: &RefCell<GameState>,
        _egui_event_resp: egui_winit::EventResponse,
        dt: Duration)
    -> Result<(), wgpu::CurrentSurfaceTexture> {
        if !self.wgpuctx.is_ready() {
            return Ok(());
        }

        let screen_size = egui::vec2(
            self.wgpuctx.surface_config().width as f32,
            self.wgpuctx.surface_config().height as f32);
        
        let view = self.wgpuctx.prepare_surface(&wgpu::TextureViewDescriptor::default())?;
        let mut scene = SceneCtx::from_view_default(&self.wgpuctx, &view, Some("Systemic window scene"));

        self.eguictx.prepare(&self.window);

        egui::Window::new("Systemic 4X")
            .fixed_rect(egui::Rect::from_min_size(egui::pos2(0.0, 0.0), screen_size))
            .resizable(false)
            .collapsible(false)
            .title_bar(false)
            .interactable(false)
            .frame(egui::Frame::NONE)
            .show(self.eguictx.context(), 
        |ui| {
            self.ui_state.paint(game_state, ui);
            
            if self.ui_state.current_system.is_some() {
                let game_state = game_state.borrow();
                let fleets_manager = game_state.fleets();
                let current_system = &game_state.solar_systems()[self.ui_state.current_system.unwrap()];
        
                egui::CentralPanel::no_frame().show_inside(ui, |central_panel| {
                    let panel_rect = central_panel.max_rect();

                    self.tactical_map.resize(
                        panel_rect.width() as u32, 
                        panel_rect.height() as u32);

                    match self.tactical_map.prepare(
                        panel_rect,
                        &self.wgpuctx,
                        fleets_manager,
                        current_system,
                        game_state.timeman(),
                        dt) 
                    {
                        Ok(_) => {},
                        Err(_) => {
                            println!("Error in tactical map prepare");
                            panic!();
                        }
                    }

                    self.tactical_map.paint_labels(
                        current_system,
                        game_state.timeman().seconds(),
                        &panel_rect, 
                        central_panel
                    );
                });

                let mut pass = RenderPassBuilder::new(Some("Systemic window render pass"), &view)
                    .clear_color(wgpu::Color { r: 0.0, g: 0.0, b: 0.0, a: 1.0 })
                    .build_from_scene(&mut scene);
        
                //Draw the tactical map canvas.
                self.tactical_map.paint(&mut pass);
            }
        });
        self.eguictx.present(
            &self.window, 
            &self.wgpuctx, 
            scene.encoder_mut(), 
            &view);

        scene.submit(&self.wgpuctx);
        self.wgpuctx.present_surface();
        self.window.request_redraw();

        Ok(())
    }

    pub fn on_event(
        &mut self,
        event: &WindowEvent)
    -> egui_winit::EventResponse
    {
        self.eguictx.window_event(&self.window, event)
    }

    pub fn resize(
        &mut self,
        width: u32,
        height: u32
    ) {
        if width > 0 && height > 0 {
            self.wgpuctx.resize(width, height);
            self.tactical_map.resize(width, height);
            self.window.request_redraw();
        }
    }

    pub fn size(
        &self)
    -> winit::dpi::PhysicalSize<u32> {
        self.window.inner_size()
    }
}
