use std::f32::consts::PI;
use std::path::PathBuf;
use std::time::Instant;

use cgmath::{Deg, Euler, Point3, Quaternion, Rad};
use imgui::*;
use imgui::{Condition, FontSource, Ui};
use legion::systems::CommandBuffer;
use legion::world::SubWorld;
use legion::IntoQuery;
use legion::*;
use nalgebra::Quaternion as naQuaternion;
use rapier3d::dynamics::{
    IntegrationParameters, JointSet, MassProperties, RigidBodyBuilder, RigidBodySet,
};
use rapier3d::geometry::{BroadPhase, ColliderBuilder, ColliderSet, NarrowPhase};
use rapier3d::na::{Isometry3, Vector, Vector3};
use rapier3d::pipeline::{ChannelEventCollector, PhysicsPipeline};

use crate::camera::{Camera, CameraController};
use crate::components::{Collider, ImguiWindow, LoopState, Mesh, Physics, Position};
use crate::geometry;
use crate::physics::state::PhysicsState;
use crate::render::state::RenderState;
use crate::render::system::{ImguiGenericOutputLine, ImguiPerformanceProfilerLine};
use crate::runtime::state::RuntimeState;
use crate::geometry::RawMesh;

pub fn quad_color(color: [f32; 4]) -> [[f32; 4]; 4] {
    [color, color, color, color]
}

#[system]
#[write_component(Mesh)]
pub fn runtime_load(
    cmd: &mut CommandBuffer,
    world: &mut SubWorld,
    #[resource] runtime_state: &mut RuntimeState,
) {
    runtime_state.preload_meshes(PathBuf::from("./resources"));

    let entity: Entity = cmd.push((ImguiWindow {
        // a window that does everything for the performance profiler
        window: || {
            imgui::Window::new(im_str!("Performance Profiler"))
                .size([400.0, 200.0], Condition::FirstUseEver)
                .position([10.0, 10.0], Condition::FirstUseEver)
        },
        func: |ui: &Ui, a: Vec<&ImguiPerformanceProfilerLine>| {
            // ui.text(im_str!("Performance Graph"));

            let draw_list = ui.get_window_draw_list();
            let top_left = ui.cursor_screen_pos();
            let region_size = ui.content_region_avail();

            let region_size = [region_size[0] * 0.80, region_size[1]];

            // Fill rect
            let qcolor = quad_color([0.5, 0.5, 1.0, 0.1]);
            draw_list.add_rect_filled_multicolor(
                top_left,
                [top_left[0] + (region_size[0]), top_left[1] + region_size[1]],
                qcolor[0],
                qcolor[1],
                qcolor[2],
                qcolor[3],
            );

            for profiler_line in a {
                let x_scale = (region_size[0]) / 400.0;
                let y_scale = region_size[1] / profiler_line.scale_max;
                profiler_line
                    .iter_data_from_head()
                    .fold((0, 0.0f32), |accum, &fps_val| {
                        let x1 = accum.0 as f32 * x_scale + top_left[0];
                        let y1 = top_left[1] + region_size[1] - accum.1 * y_scale;
                        let x2 = (accum.0 as f32 + 1.0) * x_scale + top_left[0];
                        let y2 = top_left[1] + region_size[1] - fps_val * y_scale;
                        let p1 = [x1, y1];
                        let p2 = [x2, y2];
                        draw_list
                            .add_line(p1, p2, [1.0, 1.0, 0.0, 0.8])
                            .thickness(1.0)
                            .build();
                        (accum.0 + 1, fps_val)
                    });

                let text_x = (region_size[0] + top_left[0]);
                let text_y = top_left[1] + region_size[1]
                    - profiler_line.current_average_label().0 * y_scale;
                draw_list.add_text(
                    [text_x, text_y],
                    [1.0, 1.0, 0.0, 1.0],
                    format!(
                        "{} {:.0}",
                        profiler_line.current_average_label().1,
                        1.0 / profiler_line.current_average_label().0
                    ),
                );
            }
        },
    },));
    let entity: Entity = cmd.push((ImguiPerformanceProfilerLine::new("RenderFPS".to_string()),));

    let entity: Entity = cmd.push((ImguiWindow {
        // a window that does everything for the performance profiler
        window: || {
            imgui::Window::new(im_str!("Generic Output"))
                .size([400.0, 500.0], Condition::FirstUseEver)
                .position([50.0, 250.0], Condition::FirstUseEver)
        },
        func: |ui: &Ui, a: Vec<&ImguiGenericOutputLine>| {
            for label in a {
                ui.text(im_str!("{}", label.label));
            }
        },
    },));
}

#[system]
#[write_component(Mesh)]
pub fn runtime_spawn(
    cmd: &mut CommandBuffer,
    world: &mut SubWorld,
    #[resource] runtime_state: &mut RuntimeState,
    #[resource] renderer: &mut RenderState,
) {
    for entity in &runtime_state.get_entities() {
        match entity.type_name.as_ref() {
            // "Sprite" => {
            //
            //
            //
            //     let mesh_name = entity.mesh.clone().unwrap();
            //     let raw_mesh = match runtime_state.get_mesh(mesh_name.as_str()) {
            //         None => {
            //             log::warn!("Skipping entity with invalid mesh file {:?} ", mesh_name);
            //             continue;
            //         }
            //         Some(mesh) => mesh,
            //     };
            //
            //     let position = Position::from(entity.position.clone());
            //
            //     let mut static_body = RigidBodyBuilder::new_static()
            //         .position(Isometry3::new(
            //             Vector3::new(position.x, position.y, position.z),
            //             Vector::y(),
            //         ))
            //         .build();
            //
            //     let mesh_collider = ColliderBuilder::trimesh(
            //         raw_mesh.vertices.iter().map(|v| v.position()).collect(),
            //         raw_mesh.indices.clone(),
            //     )
            //         .build();
            //
            //     let gpu_mesh_buffer = renderer
            //         .upload_mesh_to_buffer(
            //             raw_mesh,
            //             Some(wgpu::Color {
            //                 r: 1.0,
            //                 g: 0.7,
            //                 b: 0.3,
            //                 a: 1.0,
            //             }),
            //         )
            //         .unwrap();
            //
            //     let entity: Entity = cmd.push((
            //         position,
            //         gpu_mesh_buffer,
            //         Physics {
            //             rigid_body: static_body,
            //             rigid_body_handle: None,
            //         },
            //         Collider {
            //             collider: mesh_collider,
            //             collider_handle: None,
            //         },
            //     ));
            // }
            "PhysicsEntity" => {
                let mesh_name = entity.mesh.as_ref().unwrap();
                let raw_mesh = match runtime_state.get_mesh(mesh_name.as_str()) {
                    None => {
                        log::warn!("Skipping entity with invalid mesh file {:?} ", mesh_name);
                        continue;
                    }
                    Some(mesh) => mesh,
                };

                let collider = ColliderBuilder::trimesh(
                    raw_mesh.vertices.iter().map(|v| v.position()).collect(),
                    raw_mesh.indices.clone(),
                )
                .build();

                let collider = ColliderBuilder::capsule_y(2.0, 1.0).build();


                //let mut vec = Vec::new();
                //vec.push()

                let gpu_mesh_buffer = renderer
                    .upload_mesh_to_buffer(
                        raw_mesh,
                        Some(wgpu::Color {
                            r: 1.0,
                            g: 0.7,
                            b: 0.3,
                            a: 1.0,
                        }),
                    )
                    .unwrap();

                let position = Position::from(entity.position.clone());

                let mut dynamic_body = RigidBodyBuilder::new_dynamic()
                    .can_sleep(false)
                    .mass(100.0)
                    .translation(position.x, position.y, position.z)
                    .build();

                let entity: Entity = cmd.push((
                    position,
                    gpu_mesh_buffer,
                    Physics {
                        rigid_body: dynamic_body,
                        rigid_body_handle: None,
                    },
                    Collider {
                        collider: collider,
                        collider_handle: None,
                    },
                    ImguiGenericOutputLine::new("wahoo! from a physics entity".to_string()),
                ));
            }
            "StaticMesh" => {
                let mesh_name = entity.mesh.clone().unwrap();
                let raw_mesh = match runtime_state.get_mesh(mesh_name.as_str()) {
                    None => {
                        log::warn!("Skipping entity with invalid mesh file {:?} ", mesh_name);
                        continue;
                    }
                    Some(mesh) => mesh,
                };

                let position = Position::from(entity.position.clone());

                let mut static_body = RigidBodyBuilder::new_static()
                    .position(Isometry3::new(
                        Vector3::new(position.x, position.y, position.z),
                        Vector::y(),
                    ))
                    .build();

                let mesh_collider = ColliderBuilder::trimesh(
                    raw_mesh.vertices.iter().map(|v| v.position()).collect(),
                    raw_mesh.indices.clone(),
                )
                .build();

                let gpu_mesh_buffer = renderer
                    .upload_mesh_to_buffer(
                        raw_mesh,
                        Some(wgpu::Color {
                            r: 1.0,
                            g: 0.7,
                            b: 0.3,
                            a: 1.0,
                        }),
                    )
                    .unwrap();

                let entity: Entity = cmd.push((
                    position,
                    gpu_mesh_buffer,
                    Physics {
                        rigid_body: static_body,
                        rigid_body_handle: None,
                    },
                    Collider {
                        collider: mesh_collider,
                        collider_handle: None,
                    },
                ));
            }
            "Camera" => {
                let position = Position::from(entity.position.clone());
                let entity: Entity = cmd.push((
                    Camera {
                        position: cgmath::Point3 {
                            x: position.x,
                            y: position.y,
                            z: position.z,
                        },
                        yaw: Rad(PI / 2.0),
                        pitch: Rad(PI / 2.0 + 25.0),
                    },
                    CameraController::new(5.0, 1.0),
                    ImguiGenericOutputLine::new("wahoo! from a camera".to_string()),
                ));
            }
            "Light" => {
                let mesh_name = entity.mesh.clone().unwrap();
                let raw_mesh = match runtime_state.get_mesh(mesh_name.as_str()) {
                    None => {
                        log::warn!("Skipping entity with invalid mesh file {:?} ", mesh_name);
                        continue;
                    }
                    Some(mesh) => mesh,
                };

                let position = Position::from(entity.position.clone());

                let gpu_mesh_buffer = renderer
                    .upload_mesh_to_buffer(
                        raw_mesh,
                        Some(wgpu::Color {
                            r: 1.0,
                            g: 0.7,
                            b: 0.3,
                            a: 1.0,
                        }),
                    )
                    .unwrap();

                let light_entity: Entity =
                    cmd.push((position, gpu_mesh_buffer, renderer.create_light()));
            }

            _ => {}
        }
    }
}
