#include "object_pool.h"

#include "../core/memory.h"

typedef struct BzObjectPool {
    void *objects;
    size_t stride;
    size_t numObjects;
    i32 firstFree;
} BzObjectPool;

BzObjectPool *bzObjectPoolCreate(const BzObjectPoolDesc *desc) {
    BZ_ASSERT(desc->objectSize > 0);

    // NOTE: Since object bits are used as a free list
    // when not in use, we must ensure they can hold i32.
    size_t stride = desc->objectSize;
    if (stride < sizeof(i32)) {
        stride = sizeof(i32);
    }

    size_t numBytes = sizeof(BzObjectPool) + desc->numObjects * stride;
    BzObjectPool *pool = bzAlloc(numBytes);
    *pool = (BzObjectPool) {
        .objects=pool + 1,
        .stride=stride,
        .numObjects=desc->numObjects,
        .firstFree=0,
    };

    // Link free list
    for (size_t i = 0; i < pool->numObjects - 1; i++) {
        i32 *object = bzObjectPoolGetObject(pool, i);
        *object = (i32) (i + 1);
    }
    i32 *lastObject = bzObjectPoolGetObject(pool, pool->numObjects - 1);
    *lastObject = -1;

    return pool;
}
void bzObjectPoolDestroy(BzObjectPool *pool) {
    bzFree(pool);
}

size_t bzObjectPoolCalcNumFree(BzObjectPool *pool) {
    size_t count = 0;
    i32 idx = pool->firstFree;
    while (idx != -1) {
        count++;
        i32 *object = bzObjectPoolGetObject(pool, idx);
        idx = *object;
    }
    return count;
}
void *bzObjectPool(BzObjectPool *pool) {
    if (pool->firstFree == -1)
        return NULL;
    i32 *object = bzObjectPoolGetObject(pool, pool->firstFree);
    pool->firstFree = *object;
    return object;
}
void *bzObjectPoolGetObject(BzObjectPool *pool, i32 idx) {
    BZ_ASSERT(idx >= 0 && (size_t) idx < pool->numObjects);
    return (void *) ((u8 *) pool->objects + idx * pool->stride);
}
i32 bzObjectPoolGetIdx(BzObjectPool *pool, void *object) {
    size_t objectIdx = (size_t) object - (size_t) pool->objects;
    return (i32) (objectIdx / pool->stride);
}
void bzObjectPoolRelease(BzObjectPool *pool, void *object) {
    size_t objectIdx = bzObjectPoolGetIdx(pool, object);
    BZ_ASSERT(objectIdx < pool->numObjects);
    *(i32 *) ((u8 *) pool->objects + objectIdx * pool->stride) = pool->firstFree;
    pool->firstFree = (i32) objectIdx;
}
