/*
	ZAlloc.hpp (created 9/12/10)
	Author : James Russell

	Purpose :	
	
	Memory allocation library and memory allocation tracker for ZEngine.  If ZALLOC_EXTRA_SPAMMY is defined,
	then allocations will log a message to stderr when allocation occurs indicating how much.

	License:

	TODO

*/

#pragma once

#ifndef _ZALLOC_H
#define _ZALLOC_H

#include <ZUtil/ZUtilBuild.hpp>

#include <new>

//The compiler really should be smart enough to include size_t in the global 
//namespace by default.  But, on some Linux configurations, it will drop
//it in std.  Explicitly including this doesn't hurt anything.
#include <stddef.h>

#if ZALLOC_CHECK_ALLOC

//Memory Checkpoint Struct
struct ZMemState
{
	int Checkpoint;							//Checkpoint Number
};

//Allocation Info Struct
struct ZAllocInfo 
{
	static int	CurrentAllocCount;			//Current number of unfreed allocations
	static int	CurrentCheckpoint;			//Static current checkpoint tracker

	ZAllocInfo*	Next;						//Next Info Node
	uintptr_t	Address;					//Address assigned
	size_t		Size;						//Size of the allocation
	int			Line;						//Line Number
	int			Checkpoint;					//Checkpoint Integer
	char		File[128];					//File String
};

//Adds a tracking node for an allocation
extern void ZAllocAddTrack(uintptr_t _address, size_t _size, const char *_file, int _line, int _checkpoint);

//Removes a tracking node for an allocation
extern void ZAllocRemoveTrack(uintptr_t _address);

//Dumps unfreed memory string containing a list of memory leaks
extern void ZAllocDumpUnfreed(ZMemState *_state);

//Debug Placement Operator New
extern void* operator new(size_t _size, const char *_file, int _line, int _checkpoint) throw();
extern void* operator new[](size_t _size, const char *_file, int _line, int _checkpoint) throw();

//Debug Placement Delete
extern void operator delete(void *_ptr, const char* _file, int _line, int _checkpoint);
extern void operator delete[](void *_ptr, const char* _file, int _line, int _checkpoint);

//Our arguments to znew
#define ZNEW_ARGS												(__FILE__, __LINE__, ZAllocInfo::CurrentCheckpoint)

//Macros for declaring a memory 'checkpoint' and 
//dumping memory leaks to stderr
#define ZALLOC_MEM_CHECKPOINT(stateP)							stateP->Checkpoint = AtomicAddReturn((volatile int*)&ZAllocInfo::CurrentCheckpoint, 1)
#define ZALLOC_MEM_DUMP_LEAKS(stateP)							ZAllocDumpUnfreed(stateP)

#else //!ZALLOC_CHECK_ALLOC

#define ZNEW_ARGS												(std::nothrow)

#define ZALLOC_MEM_CHECKPOINT(stateP)
#define ZALLOC_MEM_DUMP_LEAKS(stateP)

#endif //ZALLOC_CHECK_ALLOC

/* Some definitions we need for znew_static */

//This is used to indicate a static allocation (basically a type flag)
struct znew_static_t { };

//Our actual static allocation overrides
extern void* operator new(size_t _size, znew_static_t _staticAlloc);
extern void* operator new[](size_t _size, znew_static_t _staticAlloc);

/* Some Memory Management Macros */

//znew loves you!
#define znew										new ZNEW_ARGS											//Allocate with params
#define znew_notrack								new (std::nothrow)										//Allocate without tracking
#define znew_static									new (znew_static_t())									//Allocate, but register for delete onexit
#define zdelete										delete													//Delete

#define zalloc(_type, _size)						ZAlloc::Alloc<_type>(_size)								//Allocates an array (block of memory) using the ZAlloc allocation library
#define zfree(_data)								ZAlloc::Free(_data)										//Frees an array (block of memory) using the ZAlloc allocation library

#define MemCopy(_type, _dest, _src, _count)			memcpy(_dest, _src, _count * sizeof(_type))
#define MemCopyFloat(_dest, _src, _count)			memcpy(_dest, _src, _count * sizeof(float))
#define MemCopyInt(_dest, _src, _count)				memcpy(_dest, _src, _count * sizeof(int))
#define MemCopyChar(_dest, _src, _count)			memcpy(_dest, _src, _count * sizeof(char))

#define MemMove(_type, _dest, _src, _count)			memmove(_dest, _src, _count * sizeof(_type))
#define MemMoveFloat(_dest, _src, _count)			memmove(_dest, _src, _count * sizeof(float))
#define MemMoveInt(_dest, _src, _count)				memmove(_dest, _src, _count * sizeof(int))
#define MemMoveChar(_dest, _src, _count)			memmove(_dest, _src, _count * sizeof(char))

#define MemSet(_type, _dest, _val, _count)			memset(_dest, _val, _count * sizeof(_type))
#define MemSetFloat(_dest, _val, _count)			memset(_dest, _val, _count * sizeof(float))
#define MemSetInt(_dest, _val, _count)				memset(_dest, _val, _count * sizeof(int))
#define MemSetChar(_dest, _val, _count)				memset(_dest, _val, _count * sizeof(char))

class ZAlloc
{
public:
	/*
	static public Alloc
	Allocation function.  Gives back an allocated array of at least _size in length.

	@param _size - the size of the block of type T
	
	@return (T*) - an array of T of at least _size in length
	*/
	template <class T>
	static T* Alloc(size_t _size);

	/*
	public static Free

	Free function.  

	@param _data - an array allocated through the use of Alloc

	@return (void)
	*/
	template <class T>
	static void Free(T *_data);
};

template <class T>
T* ZAlloc::Alloc(size_t _size)
{
	return new (std::nothrow) T[_size];
}

template <class T>
void ZAlloc::Free(T *_data)
{
	delete [] _data;
}

#endif

