/* All of this first stuff goes in recycle_handler.h */
#include "bitmask.h"
// You must have the bitmask code and them defined in this scope.
// I use bitmask.h. 
// The code for them can be found on mudbytes. The C version is all
// That is used here.
typedef BITMASK BITLIST;
typedef struct chunk_data CHUNK_DATA;
typedef struct freed_chunk_data FREED_CHUNK_DATA;
typedef struct recycle_data RECYCLE_DATA;
typedef struct _mem_info MEM_INFO;
// A single node in the chunk_list that contains all raw memory.
struct chunk_data {
    int amount; // This is the size of the memory in this chunk.
    int used; // This is the size used of the memory in this chunk.
    void *pool; // This points to a chunk.
    BITLIST minfo_datalist; // list for all our our data.
    BITLIST minfo_inlist; // Simply a list of which minfos are in the data list.
};

// A single node pointing back to the chunks with an amount it points to.
// Latent recycled data will be reinserted into the system using this method.
struct freed_chunk_data {
    int amount; // Total in this chunk.
    int used; // How much is used out of this chunk
    void *pool; // This points to a chunk.
};

struct recycle_data {
    int size; // These nodes are of this size.
    BITLIST pool_list; // A list of memory of this size.
};

// saves info on memory give away.
struct _mem_info {
    void *pool; // memory pool in question
    int amount; // Tells us how much was requested.
    bool free;
    // We can add more info later if we ever need it.
};

extern BITLIST chunk_list;
extern BITLIST freed_chunk_list;
extern BITLIST recycle_list;

void *recycle_malloc(int);
void recycle_free(void *);
bool validate_memory(void *);
****
END OF recycle_handler.h 
****/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "merc.h"
#include "recycle_handler.h"
// These lists contain all of our memory.
// These are lists using bitmasks as a list.
// You need Runter's bitmask code for this to work.
BITLIST chunk_list;
BITLIST freed_chunk_list;
BITLIST recycle_list;


int mem_alloc=0; // total in circulation.
int mem_used=0; // currently used
int mem_recycle=0; // memory in the recycle system currently


// Size of our chunks each and every time they are called.
int iSizePerm;

// Just tells you if a pointer is allocated or not.
bool memory_validate(void *mem) {
    int value = (int) mem;
    int value2;
    bool found = false;
    CHUNK_DATA* *clist = (CHUNK_DATA**) serialize_bitmask(&chunk_list);
    int i;
    for(i = 0;clist[i];++i) {
        value2 = (int)clist[i]->pool;
        if (value < value2 &&
                value >= (value2 - clist[i]->used))
             found = true;
    }

    free (clist);
    return found;
}
// this function is called any time we need memory.
CHUNK_DATA* new_chunk() {
    CHUNK_DATA *pChunk = (CHUNK_DATA *)malloc(sizeof(CHUNK_DATA));
    pChunk->amount = iSizePerm; // Chunk is always of iSizePerm.
                                // It is indeed likely we could assume that we
                                // wouldn't to match the initial load if we set the
                                // initial chunk to almost as much as it is required
                                // to run the program, but in reality in my tests
                                // I have found it's best to set this value about
                                // 10% of the total ram and have a few more malloc
                                // calls.
    pChunk->used = 0; // Nothing used so far. We will increase this value as we use more from this chunk.
    pChunk->pool = calloc(1, iSizePerm); // Allocate iSizePerm bytes.
    mem_alloc += iSizePerm;
    set_bit(&chunk_list, (int)pChunk);
    init_bitmask(&pChunk->minfo_datalist);
    init_bitmask(&pChunk->minfo_inlist);
    return pChunk;
}

bool memory_initd = false;
// Initiate our recyclable memory by a certain amount.
// Each time it needs to call a chunk it will reuse this
// value. It's best to make this more than half of your total
// memory usage.
void init_memory(int iSize) {
    iSizePerm = iSize;
    memory_initd = true;

    // Initialize all of our lists.
    init_bitmask(&chunk_list);
    init_bitmask(&freed_chunk_list);
    init_bitmask(&recycle_list);

    new_chunk(); // Get our first chunk.
}
void new_recycle(int ofsize) {
    RECYCLE_DATA *p = (RECYCLE_DATA *)malloc(sizeof(RECYCLE_DATA));
    p->size = ofsize;
    init_bitmask(&p->pool_list);
    set_bit(&recycle_list, (int)p);
}

// find some data on a pointer we've given away
MEM_INFO* find_mdata(void *trash) {
    CHUNK_DATA **clist = (CHUNK_DATA **) serialize_bitmask(&chunk_list);
    int i;
    MEM_INFO *found = NULL;
    for(i = 0;clist[i];++i) {
        // now let's see if the trash is ever here.
        if (is_set(&clist[i]->minfo_inlist, (int)trash)) { // Yes, it is here.
            MEM_INFO **mlist = (MEM_INFO **) serialize_bitmask(&clist[i]->minfo_datalist);
            int z;
            for(z = 0;mlist[z];z++) {
                if (mlist[z]->pool == trash) {
                    found = mlist[z];
                    free(mlist);
                    break;
                }
            }
            break;
        }
    }
    free(clist);
    return found;
}
// recursive function to add some memory to the trash list.
void recycle_free(void *trash) {
    // We'll free some trash and put it back in the chunk list.
    RECYCLE_DATA **rlist = (RECYCLE_DATA **) serialize_bitmask(&recycle_list);
    bool found = false;
    int i;
    MEM_INFO*pMemData = find_mdata(trash);
    int ofsize;

    if (pMemData->free == TRUE) {
        // was already free
        free (rlist);
        return;
    }
    ofsize = pMemData->amount;

    for(i = 0;rlist[i];++i) {
        if (ofsize == rlist[i]->size) {
            set_bit(&rlist[i]->pool_list, (int)trash);
            found = true;
            mem_used -= ofsize;
            mem_recycle += ofsize;
            pMemData->free = TRUE;
            break;
        }
    }
    free(rlist);

    if (found == false) {
        new_recycle(ofsize);
        recycle_free(trash);
    }
}

// Finds some recycled memory of a certain size, if it exists.
void *get_recycled(int ofsize) {
    RECYCLE_DATA **rlist = (RECYCLE_DATA **) serialize_bitmask(&recycle_list);
    int i;
    void *ptr = 0;
    for(i = 0;rlist[i];++i)
        if(rlist[i]->size == ofsize)
            ptr = pop_bitmask(&rlist[i]->pool_list);
    free(rlist);
    if (ptr) { mem_used += ofsize; mem_recycle -= ofsize; }
    return ptr;
}

void new_minfo(CHUNK_DATA *pChunk, int ofsize) {
    MEM_INFO *pMemInfo = (MEM_INFO *) malloc(sizeof(struct _mem_info));

    pMemInfo->free = FALSE;
    pMemInfo->amount = ofsize;
    pMemInfo->pool = pChunk->pool;
    set_bit(&pChunk->minfo_datalist, (int)pMemInfo);
    set_bit(&pChunk->minfo_inlist, (int)pChunk->pool);
}

// recursive function to grab some memory.
void *recycle_malloc(int ofsize) {
    CHUNK_DATA **pChunkList;
    int i;
    void *ptr=0;
    int found = FALSE;

    if (!memory_initd) init_memory(50000); // init our memory system.

    // First let's look through our recycled memory for one of exactly this size.
    ptr = get_recycled(ofsize);
    if (ptr) {
        MEM_INFO*pMemData = find_mdata(ptr);
        pMemData->free = FALSE;
        return ptr;
    }
    pChunkList = (CHUNK_DATA**) serialize_bitmask(&chunk_list);
    // Look for a chunk with enough free memory to give us our request.
    for(i = 0;pChunkList[i];++i) {
        if ((pChunkList[i]->amount - pChunkList[i]->used) < ofsize)
             // Not enough in this chunk, continue on in the loop.
             continue;

        new_minfo(pChunkList[i], ofsize);

        mem_alloc -= ofsize;
        mem_used += ofsize;
        // There's enough so let's grab a piece of it.
        pChunkList[i]->used += ofsize; // We're using more now.
        ptr = pChunkList[i]->pool;  // This is what we'll end up returning.
        found = TRUE;
        pChunkList[i]->pool = (void*)((int)(pChunkList[i]->pool) +  ofsize); // Next location we'll pull from.
        break;
    }
    free(pChunkList); // free the list we created with serialize_bitmask.

    if (found == FALSE) { // This means nothing was found, so we need to create a new chunk.
                    // EDIT: It was brought to my attention that if the amount called
                    // was bigger than the chunk that we'd hit an infinite loop.
                    // So we're going to set the chunk size to the amount of the call if
                    // the amount requested is bigger than our var
        if (iSizePerm < ofsize)
            iSizePerm = ofsize;
        // Okay, so we need to create a new chunk.
        new_chunk(); // creates a new chunk. This shoudl be big enough.
    }
    return ptr ? ptr : recycle_malloc(ofsize);
}

// Dumps debug information to a string. You can do whatever you want to with said string.

char *dump_mem_info() {
    static char buf[512];
    CHUNK_DATA **clist = (CHUNK_DATA**) serialize_bitmask(&chunk_list);
    int i, count = 0, totmem = 0;
    int pieces = 0, free_pieces= 0;
    int total_pieces_data = 0;
    for(i = 0;clist[i];++i) {
        MEM_INFO **mlist = (MEM_INFO**) serialize_bitmask(&clist[i]->minfo_datalist);
        int z;
        for(z = 0;mlist[z];++z) {
            ++pieces;
            if(mlist[z]->free)
                ++free_pieces;
            total_pieces_data += mlist[z]->amount;
        }
        free(mlist);
        totmem += clist[i]->amount;
        count++;
    }
    free(clist);
    sprintf(buf, "           Total pieces defined: %d pieces\r\n"
                 " Of those, pieces in active use: %d pieces\r\n"
                 "Of those, pieces in recycle bin: %d pieces\r\n"
                 "             For Verified Total: %d bytes\r\n"
                 " --- --- --- --- --- --- --- --- --- ---\r\n"
                 "        Memory currently in use: %d bytes\r\n"
                 "   Memory currently standing by: %d bytes\r\n"
                 "Memory currently in recycle bin: %d bytes\r\n"
                 " --- --- --- --- --- --- --- --- --- ---\r\n"
                 "  %3d chunks for Verified Total: %d bytes\r\n",pieces, pieces - free_pieces, free_pieces, total_pieces_data,
                                                                  mem_used, mem_alloc, mem_recycle, count, totmem);
    return buf;
}

// Command function for testing and values.
void do_memtest(CHAR_DATA *ch, char *argument) {
    send_to_char(dump_mem_info(), ch);
}