#pragma once

#include "pxl8_3d_camera.h"
#include "pxl8_math.h"
#include "pxl8_mesh.h"
#include "pxl8_types.h"

typedef struct pxl8_gfx pxl8_gfx;

#define PXL8_MAX_LIGHTS 16

typedef struct pxl8_light {
    pxl8_vec3 position;
    u8 r, g, b;
    u8 intensity;
    f32 radius;
    f32 radius_sq;
    f32 inv_radius_sq;
} pxl8_light;

static inline pxl8_light pxl8_light_create(pxl8_vec3 pos, u8 r, u8 g, u8 b, u8 intensity, f32 radius) {
    f32 radius_sq = radius * radius;
    return (pxl8_light){
        .position = pos,
        .r = r, .g = g, .b = b,
        .intensity = intensity,
        .radius = radius,
        .radius_sq = radius_sq,
        .inv_radius_sq = radius_sq > 0.0f ? 1.0f / radius_sq : 0.0f,
    };
}

typedef struct pxl8_3d_uniforms {
    u8 ambient;
    pxl8_vec3 celestial_dir;
    f32 celestial_intensity;
    u8 fog_color;
    f32 fog_density;
    pxl8_light lights[PXL8_MAX_LIGHTS];
    u32 num_lights;
    f32 time;
} pxl8_3d_uniforms;

typedef struct pxl8_3d_frame {
    pxl8_3d_uniforms uniforms;
    pxl8_vec3 camera_dir;
    pxl8_vec3 camera_pos;
    f32 far_clip;
    f32 near_clip;
    pxl8_mat4 projection;
    pxl8_mat4 view;
} pxl8_3d_frame;

#ifdef __cplusplus
extern "C" {
#endif

void pxl8_3d_begin_frame(pxl8_gfx* gfx, const pxl8_3d_camera* camera, const pxl8_3d_uniforms* uniforms);
void pxl8_3d_clear(pxl8_gfx* gfx, u8 color);
void pxl8_3d_clear_depth(pxl8_gfx* gfx);
void pxl8_3d_draw_line(pxl8_gfx* gfx, pxl8_vec3 v0, pxl8_vec3 v1, u8 color);
void pxl8_3d_draw_mesh(pxl8_gfx* gfx, const pxl8_mesh* mesh, const pxl8_mat4* model, const pxl8_gfx_material* material);
void pxl8_3d_draw_mesh_wireframe(pxl8_gfx* gfx, const pxl8_mesh* mesh, pxl8_mat4 model, u8 color);
void pxl8_3d_end_frame(pxl8_gfx* gfx);
u8* pxl8_3d_get_framebuffer(pxl8_gfx* gfx);
const pxl8_frustum* pxl8_3d_get_frustum(pxl8_gfx* gfx);

pxl8_3d_frame pxl8_3d_frame_from_camera(const pxl8_3d_camera* camera, const pxl8_3d_uniforms* uniforms);

#ifdef __cplusplus
}
#endif
