use cgmath::{Vector4, vec4};

use crate::{input::InputState, perf::stddev, text::{create_text_object, update_text}, vulkan::{MeshHandle, TextVertex, Vertex, VulkanRenderer, gameobject::{GameObject, GameObjectHandle, Updatable}, mesh::{CPUMesh, CPUVertexList}}};

use super::{GameState, TestGame};

pub struct FpsCounter {
    pub game_object: GameObjectHandle,
    pub text: String,
}

impl FpsCounter {
    pub fn new(game: &mut TestGame, renderer: &mut VulkanRenderer) -> FpsCounter {
        let text_mesh = create_text_object(&mut game.game_state.brush, renderer, "", 30.);
        FpsCounter { game_object: game.add_game_object(renderer, text_mesh), text: String::new() }
    }
}

impl Updatable for FpsCounter {
    fn update(&mut self, _delta_time: f32, input: &InputState, game_state: &mut GameState, game_objects: &mut Vec<GameObject>, renderer: &mut VulkanRenderer) {
        if input.button_just_pressed("toggle_framerate") {
            let go = &mut game_objects[self.game_object];
            go.visible = !go.visible;
        }

        unsafe {
            let mut counters = vec![];
            self.text.clear();

            if let Some(pcs) = &crate::perf::PERF_COUNTERS {
                for (name, pc) in pcs {
                    let mean = pc.mean();
                    let stddev = stddev(&pc.values, mean as i128);
                    let str = format!("{}: {:.1}ms (±{:.1})\n", name, (mean as f64) / 1000., stddev / 1000.);
                    counters.push((mean, str));
                }
            }

            counters.sort_by(|a, b| a.cmp(b));
            for (_, str) in counters {
                self.text.push_str(&str);
            }

            let frame_time_mean: u128 = renderer.fps_counter.iter().sum::<u128>() / crate::perf::PERF_COUNTER_SIZE as u128;
            let fps_mean = 1000000. / frame_time_mean as f64;
            let fps_min = 1000000. / *renderer.fps_counter.iter().max().unwrap() as f64;
            
            self.text.push_str(&format!("Total FPS: {:.0} (min {:.0})", fps_mean, fps_min));
        }
        update_text(self.game_object, &self.text, 20., renderer, &mut game_state.brush, game_objects);
    }
}

pub struct WorldQuad {
    pub game_object: GameObjectHandle,
}

impl WorldQuad {
    pub fn new(game: &mut TestGame, renderer: &mut VulkanRenderer) -> WorldQuad {
        let quad_verts = vec![
            Vertex { position: [0., 0., 0.], uv: [0., 0.], normal: [0., 1., 0.], tangent: [1., 0., 0., 1.], bone_index: [0, 0, 0, 0], bone_weight: [0., 0., 0., 0.] },
            Vertex { position: [1., 0., 0.], uv: [1., 0.], normal: [0., 1., 0.], tangent: [1., 0., 0., 1.], bone_index: [0, 0, 0, 0], bone_weight: [0., 0., 0., 0.] },
            Vertex { position: [0., 0., 1.], uv: [0., 1.], normal: [0., 1., 0.], tangent: [1., 0., 0., 1.], bone_index: [0, 0, 0, 0], bone_weight: [0., 0., 0., 0.] },
            Vertex { position: [1., 0., 1.], uv: [1., 1.], normal: [0., 1., 0.], tangent: [1., 0., 0., 1.], bone_index: [0, 0, 0, 0], bone_weight: [0., 0., 0., 0.] },
        ];
        let cpu_mesh = CPUMesh { vertices: CPUVertexList::Vertex3D(quad_verts), indices: vec![0, 2, 1, 1, 2, 3], local_texture_index: None, local_normal_map_index: None, name: None };
        let mesh_index = renderer.upload_mesh(cpu_mesh, None);
        let mesh_handle = MeshHandle {
            index: mesh_index,
            textures: vec![1, 0],
            original_path: None,
            pipeline_index: 0
        };
        let game_object = game.add_game_object(renderer, mesh_handle);
        WorldQuad { game_object }
    }
}

pub struct UiQuad {
    pub game_object: GameObjectHandle,
    pub background_color: Vector4<f32>,
}

impl UiQuad {
    pub fn new(game: &mut TestGame, renderer: &mut VulkanRenderer) -> UiQuad {
        let quad_verts = vec![
            TextVertex { position: [0., 0., 0.], uv: [0., 0.] },
            TextVertex { position: [0., 1., 0.], uv: [0., 1.] },
            TextVertex { position: [1., 0., 0.], uv: [1., 0.] },
            TextVertex { position: [1., 1., 0.], uv: [1., 1.] },
        ];
        let cpu_mesh = CPUMesh { vertices: CPUVertexList::VertexText(quad_verts), indices: vec![0, 2, 1, 1, 2, 3], local_texture_index: None, local_normal_map_index: None, name: None };
        let mesh_index = renderer.upload_mesh(cpu_mesh, None);
        let mesh_handle = MeshHandle {
            index: mesh_index,
            textures: vec![],
            original_path: None,
            pipeline_index: 1
        };
        let go = game.add_game_object(renderer, mesh_handle);
        UiQuad { game_object: go, background_color: vec4(0., 0., 255., 0.) }
    }
}

impl Updatable for UiQuad {
    fn update(&mut self, _delta_time: f32, _input: &InputState, _game_state: &mut GameState, _game_objects: &mut Vec<GameObject>, _renderer: &mut VulkanRenderer) {
        
    }
}