#include "transform.hpp"


const GLfloat LookAt::id[] = {
    1.0f, 0.0f, 0.0f, 0.0f,
    0.0f, 1.0f, 0.0f, 0.0f,
    0.0f, 0.0f, 1.0f, 0.0f,
    0.0f, 0.0f, 0.0f, 1.0f
};


LookAt::LookAt(const Window* w, GLuint m, GLuint v, GLuint p): window(w), uniModel(m), uniView(v), uniProj(p) {
    viewport = glm::vec4(0.0f, 0.0f, window->w, window->h);
    glUniformMatrix4fv(uniModel, 1, GL_FALSE, id);
    glUniformMatrix4fv(uniView, 1, GL_FALSE, id);
    glUniformMatrix4fv(uniProj, 1, GL_FALSE, id);
}


int LookAt::project(int cam_angle, GLfloat view_x, GLfloat view_y) {
    cam_angle = MAX(20, MIN(90, cam_angle));

    // camera distance is that we see everything at the given fov
    static const GLfloat fov = 45.0f;
    static const GLfloat g = glm::radians(180.0f - 90.0f - (fov/2.0f));
    static const GLfloat x = 1.0f / glm::cos(g);
    static const GLfloat cam_dist = glm::sqrt(x*x - 1);

    view = glm::lookAt(
        glm::vec3( // camera position
            view_x,
            -cam_dist*glm::cos(glm::radians((float)cam_angle)),
            cam_dist*glm::cos(glm::radians(90.0f-(float)cam_angle))
        ),
        glm::vec3(view_x, view_y, 0.0f), // camera target
        glm::vec3(0.0f, 1.0f, 0.0f) // up vector in positive y direction
    );
    glUniformMatrix4fv(uniView, 1, GL_FALSE, glm::value_ptr(view));

    // TODO: pre-compute model*view*proj and bind result only
    proj = glm::perspective(glm::radians(fov), 1.0f, 1.0f, 10.0f); // we take care of the ratio for ourselves, so no (float)window->w/(float)window->h needed here
    glUniformMatrix4fv(uniProj, 1, GL_FALSE, glm::value_ptr(proj));

    return cam_angle;
}


void LookAt::unproject(double x, double y, float z, GLfloat& xx, GLfloat& yy) const {
    glm::vec3 pos = glm::vec3(x, y, z);
    glm::vec3 un = glm::unProject(pos, view, proj, viewport);
    xx = un.x;
    yy = un.y;
}