#include "runtime/config.h"
#include "runtime/runtime.h"

#include "render_mesh.h"

#include <stdlib.h>

RT_CVAR_SZ(rt_MeshletVertexBufferPoolSize,
           "Amount of memory to allocate for meshlet vertex data in megabytes. Default: 512",
           512);
RT_CVAR_SZ(rt_MeshletPrimitiveIndexBufferPoolSize,
           "Amount of memory to allocate for meshlet primitive index data in megabytes. Default: 512",
           512);

/* We manage the pools in chunks with RT_MESHLET_MAX_VERTICES/RT_MESHLET_MAX_INDICES size.
 * A free chunk contains a pointer to the next free chunk.
 *
 *
 * This is _not_ the management of GPU side buffers!
 */

typedef struct rt_free_chunk {
    struct rt_free_chunk *next;
} rt_free_chunk;

typedef struct {
    rt_free_chunk *first_free;
    void *base;
    size_t num_chunks;
    size_t chunk_size;

    /* For statistics */
    size_t used;
} rt_pool;

static rt_pool _vertex_pool;
static rt_pool _index_pool;

static rt_pool CreatePool(size_t total_size, size_t chunk_size) {
    RT_ASSERT(chunk_size >= sizeof(rt_free_chunk), "Invalid chunk size");

    rt_pool pool;
    pool.base = malloc(total_size);
    if (!pool.base) {
        rtLog("RENCOM", "Meshlet pool allocation failed.");
        return pool;
    }
    pool.num_chunks = total_size / chunk_size;
    pool.chunk_size = chunk_size;
    pool.used       = 0;

    pool.first_free = pool.base;
    for (size_t i = 0; i < pool.num_chunks; ++i) {
        char *next = (i < pool.num_chunks - 1) ? (char *)pool.base + (i + 1) * chunk_size : NULL;
        char *at   = (char *)pool.base + i * chunk_size;
        rt_free_chunk *chunk = (rt_free_chunk *)at;
        chunk->next          = (rt_free_chunk *)next;
    }

    return pool;
}

rt_result InitMeshletPools(void) {
    rtRegisterCVAR(&rt_MeshletVertexBufferPoolSize);
    rtRegisterCVAR(&rt_MeshletPrimitiveIndexBufferPoolSize);

    _vertex_pool = CreatePool(RT_MB(rt_MeshletVertexBufferPoolSize.sz),
                              sizeof(rt_vertex) * RT_MESHLET_MAX_VERTICES);
    if (!_vertex_pool.base)
        return RT_OUT_OF_MEMORY;
    _index_pool =
        CreatePool(RT_MB(rt_MeshletPrimitiveIndexBufferPoolSize.sz), RT_MESHLET_MAX_INDICES);
    if (!_index_pool.base) {
        free(_vertex_pool.base);
        _vertex_pool.base = NULL;
        return RT_OUT_OF_MEMORY;
    }
    return RT_SUCCESS;
}

void ShutdownMeshletPools(void) {
}