#pragma once

#include "Controllable.hpp"
#include "core/geometry/Frustum.hpp"
#include "core/geometry/Ray.hpp"

/// Moving perspective camera
class Camera {
public:
    static std::pair<world::cell_pos, world::offset_pos> Split(const world::world_pos&);

    struct options {
        float fov = glm::radians(70.f);
        float near_dist = 0.1;
        float far_dist = 64;
    };

    Camera(const world::world_pos* absolute, const Controllable*, const options&);
    ~Camera();

    void update();
    void setOptions(const options &options) {
        o = options;
        updateProjection();
    }
    void setOrigin(const world::world_pos* abs, const Controllable* ct) {
        absolute = abs;
        origin = ct;
    }

    inline geometry::Frustum getFrustum() const { return geometry::Frustum(ViewMatrix, ProjectionMatrix); }
    inline geometry::Ray getRay() const { return geometry::Ray(*absolute, origin->getDirection(), o.far_dist); }

    constexpr glm::mat4 getViewMatrix() const { return ViewMatrix; }
    constexpr glm::mat4 getProjectionMatrix() const { return ProjectionMatrix; }
    constexpr float getDepth() const { return o.far_dist; }

private:
    const world::world_pos *absolute;
    const Controllable* origin;

    glm::mat4 ViewMatrix;
    glm::mat4 ProjectionMatrix;
    void updateProjection();

    options o;
};
