//
// Created by slinky on 5/3/26.
//

#include "FreeCamera.h"

#include "glm/ext/matrix_transform.hpp"
#include "glm/ext/matrix_clip_space.hpp"

FreeCamera::FreeCamera(const glm::vec3 &pos, float pitch, float yaw, float aspectRatio) : _position(pos), _yaw(yaw), _pitch(pitch), _aspectRatio(aspectRatio) {
    update_camera_vectors();
    update_view();
    update_projection();
}

const glm::mat4& FreeCamera::view() const {
    return _view;
}

const glm::mat4& FreeCamera::projection() const {
    return _projection;
}

const glm::vec3& FreeCamera::position() const {
    return _position;
}

void FreeCamera::rotate_pitch(float offset) {
    _pitch += offset;

    if (_pitch < MIN_PITCH) {
        _pitch = MIN_PITCH;
    }

    if (_pitch > MAX_PITCH) {
        _pitch = MAX_PITCH;
    }

    update_camera_vectors();
    update_view();
}

void FreeCamera::rotate_yaw(float offset) {
    _yaw += offset;
    update_camera_vectors();
    update_view();
}

void FreeCamera::move(CAMERA_MOVEMENT direction, float offset) {
    glm::vec3 finalDirection = {};

    switch (direction) {
    case FORWARD:
        finalDirection = front;
        break;
    case BACKWARD:
        finalDirection = -front;
        break;
    case LEFT:
        finalDirection = -right;
        break;
    case RIGHT:
        finalDirection = right;
        break;
    case UP:
        finalDirection = up;
        break;
    case DOWN:
        finalDirection = -up;
        break;
    }

    finalDirection = glm::normalize(finalDirection);
    _position += finalDirection * offset;
    _view = glm::lookAt(_position, _position + front, up);
}

void FreeCamera::update_aspect_ratio(float aspectRatio) {
    _aspectRatio = aspectRatio;
    _projection = glm::perspective(glm::radians(75.f), _aspectRatio, 0.1f, 100.0f);
}

void FreeCamera::update_camera_vectors() {
    front = {};
    front.x = cos(glm::radians(_yaw)) * cos(glm::radians(_pitch));
    front.y = sin(glm::radians(_pitch));
    front.z = sin(glm::radians(_yaw)) * cos(glm::radians(_pitch));

    right = glm::normalize(glm::cross(front, WORLD_UP));
    up = glm::normalize(glm::cross(right, front));
}

void FreeCamera::update_projection() {
    _projection = glm::perspective(glm::radians(75.f), _aspectRatio, 0.1f, 100.0f);
}

void FreeCamera::update_view() {
    _view = glm::lookAt(_position, _position + front, up);
}