#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "deftypes.h"
#include "debug.h"
#include "loadroms.h" // load_error

static UINT8 *MemoryPool[256];	// Pointers to allocated memory areas
static UINT32 MemSize[256];     // Size of each segment
static int MemoryPoolCount;	// Number of items in memory pool
static int MemoryPoolSize;	// Size of all items in memory pool

// AllocateMem():
// Allocates a space memory, size bytes long
// Returns a pointer on success, or NULL on failiure. Also generates
// a raine error message on failure...

UINT8 *AllocateMem(UINT32 size)
{
   UINT8 *memptr;

   if (size < 8) {
     printf("received size %d\n",size);
     size = 8;
   }

   memptr = (UINT8*) malloc(size);

   if(memptr)
   {

      /*

      check memory pool is not full up (unlikely)

      */

#ifdef RAINE_DEBUG
      if(MemoryPoolCount == 256)
         print_debug("alloc_mem(0x%08X) OVERFLOW [0x%02X blocks; 0x%08X total]\n", size, MemoryPoolCount, MemoryPoolSize);
#endif

      /*

      add to memory pool list

      */

      MemSize[MemoryPoolCount] = size;
      MemoryPool[MemoryPoolCount++] = memptr;
      MemoryPoolSize += size;
      // print_debug("alloc_mem(0x%08X) [0x%02X blocks; 0x%08X total]\n", size, MemoryPoolCount, MemoryPoolSize);

      return memptr;

   }
   else
   {

      /*

      error (for gui)

      */

      sprintf(
         load_debug+strlen(load_debug),
         "ERROR: Unable to allocate memory\n"
         "\n"
         "Failed to obtain 0x%08X bytes\n"
         "\n"
         "To fix this, buy more ram, or increase the available dpmi memory to 65535kb. "
         "Read rainefaq.txt for help with this (don't email us about it, thanks).\n"
         "\n",
         size
      );

      load_error |= LOAD_FATAL_ERROR;

      // print_debug("alloc_mem(0x%08X) FAILED [0x%02X blocks; 0x%08X total]\n", size, MemoryPoolCount, MemoryPoolSize);

      return NULL;

   }

}

// FreeMem():
// Deallocates a specific memory resource, memptr

void FreeMem(UINT8 *memptr)
{
   int ta,found=0;

   if(memptr)
   {
      free(memptr);

      for(ta = 0; ta < MemoryPoolCount; ta ++)
      {
	if(MemoryPool[ta] == memptr) {
	  MemoryPool[ta] = NULL;
	  MemoryPoolSize -= MemSize[ta];
	  found = 1;
	}
      }

      if (!found) {
	print_debug("FreeMem called with an unknown adr !!!\n");
      }
   }
}

// ResetMemoryPool():
// Call this to start/reset the ingame memory allocation list. Should
// be called only in one place, before loadgame.

void ResetMemoryPool(void)
{
   int ta;

   for(ta=0;ta<256;ta++){
      MemoryPool[ta]=NULL;
   }

   MemoryPoolCount=0;
   MemoryPoolSize=0;
}

int GetMemoryPoolSize() {
  return MemoryPoolSize;
}

void FreeMemoryPool(void)
{
   int ta;

   //print_debug("START: FreeMemoryPool();\n");
   //print_debug("Before memory free: 0x%08x\n", malloc(0));

   for(ta=0;ta<MemoryPoolCount;ta++){
      if(MemoryPool[ta]!=NULL) {
	// print_debug("free pool %d\n",ta);
	FreeMem(MemoryPool[ta]);
      }
   }

   ResetMemoryPool();

   //print_debug("END: FreeMemoryPool();\n");
}

#ifdef MEMORY_DEBUG

#undef malloc
#undef realloc
#undef free

struct sMemEntry
{
   void * entry;
   char * filename;
   UINT32  line;

   struct sMemEntry * next;
};

static struct sMemEntry * list = NULL;

// Init the memory list

void mbInitPurify(void)
{
   if (list)
      DonePurify();

   list = NULL;
}

// Clear the list, reporting leaked memory

void mbDonePurify(void)
{
   struct sMemEntry * node = list;

   while (node)
   {
      struct sMemEntry * tempnode = node;

      print_debug("Memory Leak in <%s> at line %d  ->  %08xh\n", node->filename, node->line, node->entry);
      free(node->entry);	// Don't free, incase it is still in use

      node = node->next;
      free(tempnode);
   }

   list = NULL;
}

// malloc replacement

void *mymalloc(UINT32 size, char * fname, UINT32 fline)
{
   struct sMemEntry * node;

   if (!size){			// Malloc(0) is used to get actual Heap Pointer
      print_debug("mymalloc: malloc(0) in file <%s> at line %d.\n", fname, fline);
      return malloc(size); 		// thus is not a memory leak
   }

   node = malloc(sizeof(struct sMemEntry));

   node->next = list;
   node->filename = fname;
   node->line = fline;
   node->entry = malloc(size);

   list = node;

   return node->entry;
}

// realloc replacement

void *myrealloc(void *ptr, UINT32 size, char *fname, UINT32 fline)
{
   struct sMemEntry *node;
   struct sMemEntry *prev;
   struct sMemEntry *next;

   if(ptr!=NULL){

   prev = NULL;
   node = list;

   while(node){

      next = node->next;

      if(node->entry == ptr){
         if(prev != NULL){
            prev->next = next;
         }
         else{
            list = next;
         }
         free(node);
      }
      else{
         prev = node;
      }
      node = next;
   }

   }

   // -----

   node = malloc(sizeof(struct sMemEntry));

   node->next = list;
   node->filename = fname;
   node->line = fline;
   node->entry = realloc(ptr, size);

   list = node;

   return node->entry;
}

// free replacement

void myfree(void * ptr, char * fname, UINT32 fline)
{
   int found;
   struct sMemEntry *node;
   struct sMemEntry *prev;
   struct sMemEntry *next;

   found = 0;

   prev = NULL;
   node = list;

   while(node){

      next = node->next;

      if(node->entry == ptr){
         if(prev != NULL){
            prev->next = next;
         }
         else{
            list = next;
         }
         free(node->entry);
         free(node);
         found++;
      }
      else{
         prev = node;
      }
      node = next;
   }

#ifdef RAINE_DEBUG
   if(!found){
      print_debug("myfree: Trying to delete unexistant pointer $%08x in file <%s> at line %d.\n", ptr, fname, fline);
   }
   if(found > 1){
      print_debug("myfree: Pointer was found %d times $%08x in file <%s> at line %d.\n", found, ptr, fname, fline);
   }
#endif
}

#endif
