use std::f32::consts::{FRAC_PI_2, PI};
use std::time::{Duration, Instant};

use cgmath::{Decomposed, InnerSpace, Matrix4, Point3, Rad, Vector3};
use winit_24::dpi::{LogicalPosition, PhysicalPosition};
use winit_24::event::{ElementState, MouseScrollDelta, VirtualKeyCode};
use crate::render::OPENGL_TO_WGPU_MATRIX;
use imgui::Condition;
use imgui::*;

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Camera {
    pub position: Point3<f32>,
    pub yaw: Rad<f32>,
    pub pitch: Rad<f32>,
}

impl Camera {
    pub fn new<V: Into<Point3<f32>>, Y: Into<Rad<f32>>, P: Into<Rad<f32>>>(
        position: V,
        yaw: Y,
        pitch: P,
    ) -> Self {
        Self {
            position: position.into(),
            yaw: yaw.into(),
            pitch: pitch.into(),
        }
    }

    pub fn calc_matrix(&self, projection: cgmath::Matrix4<f32>) -> Matrix4<f32> {

        let view_vector = Point3::new(
            (1.0 * self.pitch.0.sin() * self.yaw.0.sin()),
            (1.0 * self.pitch.0.cos()),
            (1.0 * self.pitch.0.sin() * self.yaw.0.cos()),
        );

        let mx_view = Matrix4::look_at(
            self.position,
            Point3::new(
                view_vector.x + self.position.x,
                view_vector.y + self.position.y,
                view_vector.z + self.position.z,
            ),
            Vector3::unit_y(),
        );
        // I don't know how this works, but it limits pitching to like
        // 70 degrees. Lame
        // let mx_view = Matrix4::look_at_dir(
        //     self.position,
        //     Vector3::new(
        //         self.yaw.0.cos(),
        //         self.pitch.0.sin(),
        //         self.yaw.0.sin(),
        //     ).normalize(),
        //     Vector3::unit_y(),
        // );
        let mx_correction = OPENGL_TO_WGPU_MATRIX;
        mx_correction * projection * mx_view
    }
}

#[derive(Debug)]
pub struct CameraController {
    amount_left: f32,
    amount_right: f32,
    amount_forward: f32,
    amount_backward: f32,
    amount_up: f32,
    amount_down: f32,
    rotate_horizontal: f32,
    rotate_vertical: f32,
    scroll: f32,
    speed: f32,
    sensitivity: f32,
    active: bool,
}

impl CameraController {
    pub fn new(speed: f32, sensitivity: f32) -> Self {
        Self {
            amount_left: 0.0,
            amount_right: 0.0,
            amount_forward: 0.0,
            amount_backward: 0.0,
            amount_up: 0.0,
            amount_down: 0.0,
            rotate_horizontal: 0.0,
            rotate_vertical: 0.0,
            scroll: 0.0,
            speed,
            sensitivity,
            active: true
        }
    }

    pub fn process_keyboard(&mut self, key: VirtualKeyCode, state: ElementState) -> bool {
        let amount = if state == ElementState::Pressed {
            1.0
        } else {
            0.0
        };
        let active = match key {
            VirtualKeyCode::P => {
                if state == ElementState::Pressed {
                    self.active = !self.active;
                }
                self.active
            },
            _ => {self.active}
        };
        if active {
            match key {
                VirtualKeyCode::W | VirtualKeyCode::Up => {
                    self.amount_forward = amount;
                    true
                }
                VirtualKeyCode::S | VirtualKeyCode::Down => {
                    self.amount_backward = amount;
                    true
                }
                VirtualKeyCode::A | VirtualKeyCode::Left => {
                    self.amount_left = amount;
                    true
                }
                VirtualKeyCode::D | VirtualKeyCode::Right => {
                    self.amount_right = amount;
                    true
                }
                VirtualKeyCode::Space => {
                    self.amount_up = amount;
                    true
                }
                VirtualKeyCode::LShift => {
                    self.amount_down = amount;
                    true
                }
                _ => false,
            }
        } else {
            false
        }
    }

    pub fn process_mouse(&mut self, mouse_dx: f64, mouse_dy: f64) {
        if self.active {
            self.rotate_horizontal = -mouse_dx as f32;
            self.rotate_vertical = mouse_dy as f32;
        }
    }

    pub fn update_camera(&mut self, camera: &mut Camera, dt: f32) {

        // Move forward/backward and left/right
        let view_vector = Vector3::new(
            (1.0 * camera.pitch.0.sin() * camera.yaw.0.sin()),
            (1.0 * camera.pitch.0.cos()),
            (1.0 * camera.pitch.0.sin() * camera.yaw.0.cos()),
        );

        // Offset the yaw 90 degrees and set the pitch to level for our
        // right / left hand translation vectors
        let offset = camera.yaw.0 + PI/2.0;
        let pitch = PI/2.0;
        let left_vector = Vector3::new(
            (1.0 * pitch.sin() * offset.sin()),
            (1.0 * pitch.cos()),
            (1.0 * pitch.sin() * offset.cos()),
        );

        camera.position += view_vector * (self.amount_forward - self.amount_backward) * self.speed * dt;
        camera.position += left_vector * (self.amount_left - self.amount_right) * self.speed * dt;
        camera.position.y += (self.amount_up - self.amount_down) * self.speed * dt;

        // Rotate
        camera.yaw += Rad(self.rotate_horizontal) * self.sensitivity * dt;
        camera.pitch += Rad(self.rotate_vertical) * self.sensitivity * dt;

        self.rotate_horizontal = 0.0;
        self.rotate_vertical = 0.0;

        // Keep the camera's angle from going too high/low.
        if camera.pitch < -Rad(0.001) {
            camera.pitch = -Rad(0.001);
        } else if camera.pitch > Rad(PI - 0.001) {
            camera.pitch = Rad(PI - 0.001);
        }
    }
}
