#ifndef CAMERA_H
#define CAMERA_H

#include <math.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

#include "Player.h"
#include "Frustum.h"


class Camera
{
  public:
    Player* player;
    glm::vec3 target;
    glm::vec3 direction;
    glm::vec3 up;
    glm::vec3 worldUp;

    Camera(Player* player)
    {
        this->player = player;
        this->target = glm::vec3(0.0f, 0.0f, -1.0f);

        this->direction = glm::normalize(this->player->position);
        this->worldUp = glm::vec3(0.0f, 1.0f, 0.0f);

        glm::vec3 right = glm::normalize(glm::cross(up, this->direction));
        this->up = glm::cross(this->direction, right);
    }

    glm::mat4 getView()
    {
        glm::vec3 real_camera_pos = this->player->getEyePosition();

        glm::mat4 v = glm::lookAt(real_camera_pos,
                           real_camera_pos + this->target,
                           this->worldUp);

        return v;
    }

    glm::vec3 getEyePosition() const {
        return player->getEyePosition();
    }

    // Build frustum from view-projection matrix (more reliable method)
    Frustum createFrustumFromMatrix(const glm::mat4& viewProj) {
        Frustum frustum;
        
        // Extract planes from view-projection matrix
        // Left plane
        frustum.leftFace.normal.x = viewProj[0][3] + viewProj[0][0];
        frustum.leftFace.normal.y = viewProj[1][3] + viewProj[1][0];
        frustum.leftFace.normal.z = viewProj[2][3] + viewProj[2][0];
        frustum.leftFace.distance = viewProj[3][3] + viewProj[3][0];
        
        // Right plane
        frustum.rightFace.normal.x = viewProj[0][3] - viewProj[0][0];
        frustum.rightFace.normal.y = viewProj[1][3] - viewProj[1][0];
        frustum.rightFace.normal.z = viewProj[2][3] - viewProj[2][0];
        frustum.rightFace.distance = viewProj[3][3] - viewProj[3][0];
        
        // Bottom plane
        frustum.bottomFace.normal.x = viewProj[0][3] + viewProj[0][1];
        frustum.bottomFace.normal.y = viewProj[1][3] + viewProj[1][1];
        frustum.bottomFace.normal.z = viewProj[2][3] + viewProj[2][1];
        frustum.bottomFace.distance = viewProj[3][3] + viewProj[3][1];
        
        // Top plane
        frustum.topFace.normal.x = viewProj[0][3] - viewProj[0][1];
        frustum.topFace.normal.y = viewProj[1][3] - viewProj[1][1];
        frustum.topFace.normal.z = viewProj[2][3] - viewProj[2][1];
        frustum.topFace.distance = viewProj[3][3] - viewProj[3][1];
        
        // Near plane
        frustum.nearFace.normal.x = viewProj[0][3] + viewProj[0][2];
        frustum.nearFace.normal.y = viewProj[1][3] + viewProj[1][2];
        frustum.nearFace.normal.z = viewProj[2][3] + viewProj[2][2];
        frustum.nearFace.distance = viewProj[3][3] + viewProj[3][2];
        
        // Far plane
        frustum.farFace.normal.x = viewProj[0][3] - viewProj[0][2];
        frustum.farFace.normal.y = viewProj[1][3] - viewProj[1][2];
        frustum.farFace.normal.z = viewProj[2][3] - viewProj[2][2];
        frustum.farFace.distance = viewProj[3][3] - viewProj[3][2];
        
        // Normalize all planes
        float length;
        
        length = glm::length(frustum.leftFace.normal);
        frustum.leftFace.normal /= length;
        frustum.leftFace.distance /= length;
        
        length = glm::length(frustum.rightFace.normal);
        frustum.rightFace.normal /= length;
        frustum.rightFace.distance /= length;
        
        length = glm::length(frustum.bottomFace.normal);
        frustum.bottomFace.normal /= length;
        frustum.bottomFace.distance /= length;
        
        length = glm::length(frustum.topFace.normal);
        frustum.topFace.normal /= length;
        frustum.topFace.distance /= length;
        
        length = glm::length(frustum.nearFace.normal);
        frustum.nearFace.normal /= length;
        frustum.nearFace.distance /= length;
        
        length = glm::length(frustum.farFace.normal);
        frustum.farFace.normal /= length;
        frustum.farFace.distance /= length;
        
        return frustum;
    }
};

#endif
