#include <cmath>
#include <glad/glad.h>
#include <glm/ext/matrix_float4x4.hpp>
#include <glm/ext/matrix_transform.hpp>
#include <glm/ext/vector_float3.hpp>
#include <glm/geometric.hpp>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/trigonometric.hpp>

enum CameraMovement {
    FORWARD,
    BACKWARD,
    LEFT,
    RIGHT,
};

const float YAW = -90.0f;
const float PITCH = 0.0f;
const float SPEED = 2.5f;
const float SENSITIVITY = 0.1f;
const float ZOOM = 45.0f;

class Camera {
public:
    glm::vec3 position_;
    glm::vec3 front_;
    glm::vec3 up_;
    glm::vec3 right_;
    glm::vec3 worldup_;

    float yaw_;
    float pitch_;
    float movement_speed_;
    float mouse_sensitivity_;
    float zoom_;

    Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f),
        glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f),
        float yaw = YAW, float pitch = PITCH) :
    front_(glm::vec3(0.0f, 0.0f, -1.0f)),
    movement_speed_(SPEED),
    mouse_sensitivity_(SENSITIVITY),
    zoom_(ZOOM) {
        position_ = position;
        worldup_ = up;
        yaw_ = yaw;
        pitch_ = pitch;
        update_camera_vectors();
    }

    Camera(float posx, float posy, float posz, float upx, float upy, float upz, float yaw, float pitch) :
        front_(glm::vec3(0.0f, 0.0f, -1.0f))
        , mouse_sensitivity_(SENSITIVITY),
        zoom_(ZOOM){
            position_ = glm::vec3(posx, posy, posz);
            worldup_ = glm::vec3(upx, upy, upz);
            yaw_ = yaw;
            pitch_ = pitch;
            update_camera_vectors();
    }

    glm::mat4 get_view_matrix() {
        return glm::lookAt(position_, position_ + front_, up_);
    }

    void process_keyboard(CameraMovement direction, float deltatime) {
        float velocity = movement_speed_ * deltatime;
        if (direction == FORWARD) {
            position_ += front_ * velocity;
        }
        if (direction == BACKWARD) {
            position_ -= front_ * velocity;
        }
        if (direction == LEFT) {
            position_ -= right_ * velocity;
        }
        if (direction == RIGHT) {
            position_ += right_ * velocity;
        }

        position_.y = 0.0f;
    }

    void process_mouse_movement(float xoffset, float yoffset, GLboolean constrain_pitch = true) {

    }

    void process_mouse_scroll(float yoffset) {
        zoom_ -= (float)yoffset;
        if (zoom_ < 1.0f) {
            zoom_ =  1.0f;
        }
        if (zoom_ > 45.0f) {
            zoom_ = 45.0f;
        }
    }

private:
    void update_camera_vectors() {
        glm::vec3 front;
        front.x = std::cos(glm::radians(yaw_)) * std::cos(glm::radians(pitch_));
        front.y = std::sin(glm::radians(pitch_));
        front.z = std::sin(glm::radians(yaw_)) * std::cos(glm::radians(pitch_));
        front_ = glm::normalize(front);
        right_ = glm::normalize(glm::cross(front_, worldup_));
        up_ = glm::normalize(glm::cross(right_, front_));
    }
};
