/* a rendering.c mixin */
#ifndef TRIANGLES_H
#define TRIANGLES_H

#include "townengine/context.h"
#include "internal_api.h"
#include "../textures/internal_api.h"

#include <stb_ds.h>

/* TODO: automatic handling of repeating textures */
/*       for that we could allocate a loner texture */
void unfurl_triangle(const char *path,
                     t_fvec3 v0,
                     t_fvec3 v1,
                     t_fvec3 v2,
                     t_shvec2 uv0,
                     t_shvec2 uv1,
                     t_shvec2 uv2)
{
    const t_texture_key texture_key = textures_get_key(&ctx.texture_cache, path);

    struct mesh_batch_item *batch_p = hmgetp_null(ctx.uncolored_mesh_batches, texture_key);
    if (!batch_p) {
        struct mesh_batch item = {0};
        hmput(ctx.uncolored_mesh_batches, texture_key, item);
        batch_p = &ctx.uncolored_mesh_batches[hmlenu(ctx.uncolored_mesh_batches) - 1]; /* TODO: can last index be used? */
    }

    union uncolored_space_triangle triangle = { .primitive = {
        .v0 = v0,
        .v1 = v1,
        .v2 = v2,
        .uv1 = m_to_fvec2(uv1),
        .uv0 = m_to_fvec2(uv0),
        .uv2 = m_to_fvec2(uv2),
    }};

    union uncolored_space_triangle *triangles =
        (union uncolored_space_triangle *)batch_p->value.primitives;
    arrpush(triangles, triangle);
    batch_p->value.primitives = (uint8_t *)triangles;
}


static void draw_uncolored_space_traingle_batch(struct mesh_batch *batch,
                                                t_texture_key texture_key)
{
    size_t primitives_len = arrlenu(batch->primitives);

    if (primitives_len == 0)
        return;

    /* create vertex array object */
    if (batch->buffer == 0)
        glGenBuffers(1, &batch->buffer);

    /* TODO: try using mapped buffers while building batches instead? */
    /*       this way we could skip client side copy that is kept until commitment */
    /*       alternatively we could commit glBufferSubData based on a threshold */

    /* update pixel-based uvs to correspond with texture atlases */
    for (size_t i = 0; i < primitives_len; ++i) {
        struct uncolored_space_triangle_payload *payload =
            &((union uncolored_space_triangle *)batch->primitives)[i].payload;

        const t_frect srcrect = textures_get_srcrect(&ctx.texture_cache, texture_key);
        const t_frect dims    = textures_get_dims(&ctx.texture_cache, texture_key);

        const float wr = srcrect.w / dims.w;
        const float hr = srcrect.h / dims.h;
        const float xr = srcrect.x / dims.w;
        const float yr = srcrect.y / dims.h;

        payload->uv0.x = xr + ((float)payload->uv0.x / srcrect.w) * wr;
        payload->uv0.y = yr + ((float)payload->uv0.y / srcrect.h) * hr;
        payload->uv1.x = xr + ((float)payload->uv1.x / srcrect.w) * wr;
        payload->uv1.y = yr + ((float)payload->uv1.y / srcrect.h) * hr;
        payload->uv2.x = xr + ((float)payload->uv2.x / srcrect.w) * wr;
        payload->uv2.y = yr + ((float)payload->uv2.y / srcrect.h) * hr;
    }

    textures_bind(&ctx.texture_cache, texture_key, GL_TEXTURE_2D);

    glBindBuffer(GL_ARRAY_BUFFER, batch->buffer);

    /* upload batched data */
    glBufferData(GL_ARRAY_BUFFER,
                 primitives_len * sizeof (struct uncolored_space_triangle_payload),
                 batch->primitives,
                 GL_STREAM_DRAW);

    /* vertex specification*/
    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(3,
                    GL_FLOAT,
                    offsetof(struct uncolored_space_triangle_payload, v1),
                    (void *)offsetof(struct uncolored_space_triangle_payload, v0));

    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glClientActiveTexture(GL_TEXTURE0);
    glTexCoordPointer(2,
                      GL_FLOAT,
                      offsetof(struct uncolored_space_triangle_payload, v1),
                      (void *)offsetof(struct uncolored_space_triangle_payload, uv0));

    /* commit for drawing */
    glDrawArrays(GL_TRIANGLES, 0, 3 * (GLint)primitives_len);

    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);

    /* invalidate the buffer immediately */
    glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STREAM_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

#endif
