#ifndef MEMORYMANAGER_H
#define MEMORYMANAGER_H

#include "../pnheaders/general.h"
#include "../pnheaders/matrix.h"

/*The MemoryManager structure is used to store information about dynamically
allocated memory so that it can be freed when appropriate. It stores lists
of pointers to ordinary memory blocks and pointers to matrices.*/
typedef struct MemoryManager
{
  void** memory;
  int length;
  int next;
  int addressblock;

  matrix** matrices;
  int lengthm;
  int nextm;
  int addressblockm;
} MemoryManager;

/*ManageMatrix adds a matrix to the list of matrices to free later.*/
inline void ManageMatrix(MemoryManager* mgr, matrix* m);

/*ManageMemory adds a block of memory to the list of blocks to be freed later.*/
inline void ManageMemory(MemoryManager* mgr, void* mem);

/*mmalloc allocates a block of memory and adds it to the lists.*/
inline void* mmalloc(MemoryManager* mgr, size_t nbytes);

/*mcalloc allocates a block of memory for an array and adds to the lists.*/
inline void* mcalloc(MemoryManager* mgr, size_t n, size_t nbytes);

/*MAllocateMatrixType is exactly like AllocateMatrixType except that it adds
the newly-allocated matrix to the list of matrices to be freed later.*/
inline void MAllocateMatrixType(MemoryManager* mgr, int type, matrix* m, int rows, int cols);

/*FreeMemory frees the memory blocks and matrices listed by a memory manager.*/
void FreeMemory(MemoryManager* mgr);

/*CreateMemoryManager initializes a memory manager structure. InitialLength
and InitialMatrices are the initial sizes of the lists that hold memory block
and matrix addresses, respectively. Both these parameters must be at least 1 and
will be set to 1 automatically if passed as less than that.
AddressBlockSize and MatrixAddressBlockSize are the number of additional
addresses that memory will be allocated for each time the list of memory block
addresses or matrix addresses respectively become full. These must be at least
five and will be set to five automatically if passed as less than that.*/
MemoryManager CreateMemoryManager(int InitialLength, int InitialMatrices, int AddressBlockSize, int MatrixAddressBlockSize);

/*The MemoryGrower structure maintains the information needed to have a region
of memory that can be increased in size several times before a time-consuming
reallocation is necessary.*/
typedef struct MemoryGrower
{
	void** memory;
	int *  capacity;
	int    blocksize, memories, next;
} MemoryGrower;

/*CreateMemoryGrower initializes a memory grower structure.*/
MemoryGrower CreateMemoryGrower(int pointers, int blocksize);

/*growMalloc and growCalloc grow the specified piece of memory to the given
capacity. If the pointer to the memory is null, new memory is allocated. If the
pointer points to a piece of memory the memorygrower is not responsible for the
function fails.*/
void* growMalloc(MemoryGrower* g, void* memory, int size);
void* growCalloc(MemoryGrower* g, void* memory, int elementSize, int elements);

/*FreeMemoryGrower deallocates all the memories pointed to by a memory
grower.*/
void FreeMemoryGrower(MemoryGrower* g);
#endif
