// ------------------------- VSMPOOL.C ------------------------------
// VSpace library
// Designed and written by Javier Ar‚valo Baeza.
// Memory pools for allocating (and discarding?) textures.

#include "vsmpool.h"

    // Init a memory pool of given size. Insert it in a chain of equal
    // pools. If memory pool is NULL, alloc it automagically.
extern VSMP_PPool VSMP_Init(VSMP_PPool mp, VSMP_PPool *pchain, int n, int w, int h) {

//    VSLOG("Allocating pool for %d items of %dx%d\n", n, w, h);
    if (n <= 0)
        return NULL;

    if (mp == NULL) {
        mp = (VSMP_PPool)NEW(VSMP_SIZEPOOL(n));
        if (mp == NULL)
            return NULL;
        memset(mp, 0, VSMP_SIZEPOOL(n));
        mp->alloc = TRUE;
    } else {
        memset(mp, 0, VSMP_SIZEPOOL(n));
    }
//printf("Pool allocated\n");

    assert(mp != NULL);

    mp->block = (void*)NEW(n*w*h);
    if (mp->block == NULL)  {
        if (mp->alloc)
            DISPOSE(mp);
        return NULL;
    }
//printf("Block allocated\n");
    mp->n = n;
    mp->w = w;
    mp->h = h;
    if (pchain != NULL) {
        VSMP_PPool chain;

//printf("Going to link pool\n");
        chain = *pchain;
            // Link it into the chain
        if (chain != NULL)
            mp->prev = chain->prev;
        else
            mp->prev = NULL;
        mp->next = chain;
        if (chain != NULL)
            chain->prev = mp;
        mp->prev->next = mp;
        *pchain = mp;
//printf("Pool linked\n");
    }
    return mp;
}

    // Alloc a block in a given memory pool chain. Have *addr set to NULL
    // so that we can check and avoid memory leaks.
extern bool VSMP_Alloc(VSMP_PPool *pchain, void **addr, int n, int w, int h, int action) {
    VSMP_PPool pc;
    int i;

    if (pchain == NULL || addr == NULL)
        return FALSE;
    REQUIRE(*addr == NULL);
    pc = *pchain;
    while (pc != NULL) {
        for (i = 0; i < pc->n; i++) {
            if (pc->el[i].addr == NULL)
                break;
        }
        if (i < pc->n)
            break;
        pc = pc->next;
    }
        // Couldn't find a suitable block.
    if (pc == NULL) {
        switch (action) {
            case VSMPA_FAIL:
                break;
            case VSMPA_CREATE:
                pc = VSMP_Init(NULL, pchain, n, w, h);
                if (pc != NULL)
                    i = pc->n-1;
                break;
            case VSMPA_FREE:
                    // This one needs more work. By now:
                pc = VSMP_Init(NULL, pchain, n, w, h);
                if (pc != NULL)
                    i = pc->n-1;
                break;
        }
        if (pc == NULL)
            return FALSE;
    }

        // Alloc at the end for better performance.
    pc->el[i].addr = addr;
    *addr = (void*)(((byte*)pc->block) + i*pc->w);
    return TRUE;
}

    // Dealloc a block in a given memory pool chain.
extern bool VSMP_Free(VSMP_PPool chain, void **addr) {
    VSMP_PPool pc;
    int i;

    pc = chain;
    while (pc != NULL) {
        for (i = 0; i < pc->n; i++) {
            if (pc->el[i].addr == addr) {
                *addr = NULL;
                pc->el[i].addr = NULL;
                return TRUE;
            }
        }
        pc = pc->next;
    }
    return FALSE;
}

    // Deallocate a full chain.
extern void VSMP_End(VSMP_PPool chain) {
    VSMP_PPool pc, pg;
    int i;

    if (chain == NULL)
        return;

    if (chain->prev != NULL)
        chain->prev->next = NULL;
    pc = chain;
    while (pc != NULL) {
        for (i = 0; i < pc->n; i++) {
            if (pc->el[i].addr != NULL) {
                    // Sanity check.
                REQUIRE(*pc->el[i].addr == (pc->block + i*pc->w));
                *pc->el[i].addr = NULL;
            }
        }
        DISPOSE(pc->block);
        pg = pc->next;
        if (pc->alloc)
            DISPOSE(pc);
        else
            memset(pc, 0, sizeof(*pc));
        pc = pg;
    }
}

// ------------------------- VSMPOOL.C ------------------------------
