#include "twn_draw.h"
#include "twn_draw_c.h"
#include "twn_engine_context_c.h"

#include <stb_ds.h>

#include <stdbool.h>
#include <stddef.h>


void draw_rectangle(Rect rect, Color color) {
    RectPrimitive rectangle = {
        .rect = rect,
        .color = color,
    };

    Primitive2D primitive = {
        .type = PRIMITIVE_2D_RECT,
        .rect = rectangle,
    };

    arrput(ctx.render_queue_2d, primitive);
}


struct QuadBatch collect_rect_batch(const Primitive2D primitives[], size_t len) {
    SDL_assert(primitives[0].type == PRIMITIVE_2D_RECT);
    SDL_assert(primitives && len != 0);

    struct QuadBatch batch = {
        .mode = primitives[0].rect.color.a == 255 ? TEXTURE_MODE_OPAQUE : TEXTURE_MODE_GHOSTLY,
        .constant_colored = true,
    };

    const uint32_t uniform_color = *(const uint32_t *)(void const *)&primitives[0].rect.color;

    /* batch size is clamped so that reallocated short indices could be used */
    if (len >= QUAD_ELEMENT_BUFFER_LENGTH)
        len = QUAD_ELEMENT_BUFFER_LENGTH;

    for (size_t i = 0; i < len; ++i) {
        const Primitive2D *const current = &primitives[i];

        /* don't touch things other than rectangles */
        if (current->type != PRIMITIVE_2D_RECT)
            break;

        /* only collect the same blend modes */
        if ((current->rect.color.a == 255 ? TEXTURE_MODE_OPAQUE : TEXTURE_MODE_GHOSTLY) != batch.mode)
            break;

        /* if all are modulated the same we can skip sending the color data */
        if (*(const uint32_t *)(void const *)&current->rect.color != uniform_color)
            batch.constant_colored = false;

        ++batch.size;
    }

    return batch;
}


/* assumes that orthogonal matrix setup is done already */
void render_rect_batch(const Primitive2D primitives[],
                       const struct QuadBatch batch)
{
    SDL_assert(primitives && batch.size != 0);
    SDL_assert(primitives[0].type == PRIMITIVE_2D_RECT);

    /* single vertex array is used for every batch with NULL glBufferData() trick at the end */
    VertexBuffer const vertex_array = get_scratch_vertex_array();

    /* vertex population over a vertex buffer builder interface */
    {
        VertexBufferBuilder payload = build_vertex_buffer(vertex_array, get_quad_payload_size(batch) * batch.size);

        for (size_t i = 0; i < batch.size; ++i) {
            /* render opaques front to back, to gain benefit of an early z rejection */
            const size_t cur = batch.mode == TEXTURE_MODE_GHOSTLY ? i : batch.size - i - 1;
            const RectPrimitive rect = primitives[cur].rect;

            Vec2 v0 = { rect.rect.x,               rect.rect.y };
            Vec2 v1 = { rect.rect.x,               rect.rect.y + rect.rect.h };
            Vec2 v2 = { rect.rect.x + rect.rect.w, rect.rect.y + rect.rect.h };
            Vec2 v3 = { rect.rect.x + rect.rect.w, rect.rect.y };

            push_quad_payload_to_vertex_buffer_builder(
                batch, i, &payload,
                v0, v1, v2, v3,
                (Vec2){0}, (Vec2){0}, (Vec2){0}, (Vec2){0},
                rect.color);
        }

        finish_vertex_builder(&payload);
    }

    finally_render_quads(primitives, batch, vertex_array);
}
