#include "twn_camera_c.h"
#include "twn_vec.h"
#include "twn_engine_context_c.h"

#include <math.h>


#define CAMERA_NEAR_Z 0.1f
#define CAMERA_FAR_Z 100.0f


Matrix4 camera_look_at(const Camera *const camera) {
    /* from cglm */
    const Vec3 r = m_vec_norm(m_vec_cross(camera->target, camera->up));
    const Vec3 u = m_vec_cross(r, camera->target);

    Matrix4 result;

    result.row[0].x =  r.x;
    result.row[0].y =  u.x;
    result.row[0].z = -camera->target.x;
    result.row[1].x =  r.y;
    result.row[1].y =  u.y;
    result.row[1].z = -camera->target.y;
    result.row[2].x =  r.z;
    result.row[2].y =  u.z;
    result.row[2].z = -camera->target.z;
    result.row[3].x = -m_vec_dot(r, camera->pos);
    result.row[3].y = -m_vec_dot(u, camera->pos);
    result.row[3].z =  m_vec_dot(camera->target, camera->pos);
    result.row[3].w =  1.0f;

    result.row[0].w =  result.row[1].w = result.row[2].w = 0.0f;

    return result;
}

Matrix4 camera_perspective(const Camera *const camera) {
    /* from cglm */
    Matrix4 result = {0};

    const float aspect = ((float)ctx.base_render_width / (float)ctx.base_render_height);

    const float f  = 1.0f / tanf(camera->fov * 0.5f);
    const float fn = 1.0f / (CAMERA_NEAR_Z - CAMERA_FAR_Z);

    result.row[0].x =  f / aspect;
    result.row[1].y =  f;
    result.row[2].z =  (CAMERA_NEAR_Z + CAMERA_FAR_Z) * fn;
    result.row[2].w = -1.0f;
    result.row[3].z =  2.0f * CAMERA_NEAR_Z * CAMERA_FAR_Z * fn;

    return result;
}

Matrix4 camera_orthographic(const Camera *const camera) {
    /* from cglm */
    (void)camera;

    Matrix4 result = {0};

    const float rl =  1.0f / (camera->viewbox[0].y - camera->viewbox[0].x);
    const float tb =  1.0f / (camera->viewbox[1].x - camera->viewbox[1].y);
    const float fn = -1.0f / (CAMERA_FAR_Z - -CAMERA_FAR_Z);

    result.row[0].x =  2.0f * rl;
    result.row[1].y =  2.0f * tb;
    result.row[2].z =  2.0f * fn;
    result.row[3].x = -(camera->viewbox[0].y + camera->viewbox[0].x)  * rl;
    result.row[3].y = -(camera->viewbox[1].x + camera->viewbox[1].y)  * tb;
    result.row[3].z =  (CAMERA_FAR_Z + -CAMERA_FAR_Z) * fn;
    result.row[3].w =  1.0f;

    return result;
}
