/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


#ifndef	__NALLOCATOR_INTERFACE__
#define	__NALLOCATOR_INTERFACE__


		#include "ntypes.h"
		#include <new>


namespace GS {
	namespace Alloc {

// Allocator systems.
enum	System
{
	General,

	ListItem,
	StringBuffer,

	Filesystem,
	Metatag,

	Curve,
	Motion,
	AnimationSource,

	Vector,
	Matrix,

	Physics,

	Item3d,

	Geometry,
	Material,
	Texture,
	Picture,

	Mixer,
	Sound,

	Renderer,
	RendererJob,
	RendererTerrain,

	VertexBuffer,

	Global,
	SystemCount
};

struct	SystemDesc
{
	const char	*group;
	const char	*name;
};

/*
	Allocators can be changed per system by editing the following defines.
*/
#define	MemAllocListItem			GS::Alloc::DefaultAllocator
#define	MemAllocStringBuffer		GS::Alloc::DefaultAllocator

#define	MemAllocFilesystem			GS::Alloc::DefaultAllocator
#define	MemAllocMetatag				GS::Alloc::DefaultAllocator

#define	MemAllocCurve				GS::Alloc::DefaultAllocator
#define	MemAllocMotion				GS::Alloc::DefaultAllocator
#define	MemAllocAnimationSource		GS::Alloc::DefaultAllocator

#define	MemAllocVector				GS::Alloc::DefaultAllocator
#define	MemAllocMatrix				GS::Alloc::DefaultAllocator

#define	MemAllocPhysics				GS::Alloc::DefaultAllocator

#define	MemAllocItem3d				GS::Alloc::DefaultAllocator

#define	MemAllocGeometry			GS::Alloc::DefaultAllocator
#define	MemAllocMaterial			GS::Alloc::DefaultAllocator
#define	MemAllocTexture				GS::Alloc::DefaultAllocator
#define	MemAllocPicture				GS::Alloc::DefaultAllocator

#define	MemAllocMixer				GS::Alloc::DefaultAllocator
#define	MemAllocSound				GS::Alloc::DefaultAllocator

#define	MemAllocRenderer			GS::Alloc::DefaultAllocator
#define	MemAllocRendererJob			GS::Alloc::DefaultAllocator
#define	MemAllocRendererTerrain		GS::Alloc::DefaultAllocator

#define	MemAllocVertexBuffer		GS::Alloc::DefaultAllocator

#if		__ENABLE_ALLOCATION_STAT__

struct	Header
{
	size_t		size;
};

struct	Stat
{
	uint		alloc_count, alloc_avg;
	uint		alive_count, alive_count_peak;
	size_t		size, size_peak;

	Stat() : alloc_count(0), alloc_avg(0), alive_count(0), alive_count_peak(0), size(0), size_peak(0) {}
};

		/// Per system statistics.
extern	Stat			system_stat[SystemCount];
extern	SystemDesc		system_desc[SystemCount];

		size_t			GetAdjustedAllocationSize(size_t);
		void			*SetupAllocationStat(void *, size_t);
		void			*GetAllocationStat(void *, Header *&);

		void			UpdateStatAlloc(size_t, System);
		void			UpdateStatDelete(size_t, System);

#endif

/// Standard C++ allocator.
struct	DefaultAllocator
{
static	void	*Alloc(size_t size, System sys = General);
static	void	Delete(void *addr, System sys = General);
};

	} // Alloc
} // GS

//------------------------------------------------------------------------------
#if		__ENABLE_ALLOCATION_STAT__

	#define	__NSTAT_WRAPALLOC(__WRAPPED_CALL, __SYSTEM)\
		GS::Alloc::UpdateStatAlloc(size, __SYSTEM);\
		size_t __raw_size = size;\
		size = GS::Alloc::GetAdjustedAllocationSize(size);\
		return GS::Alloc::SetupAllocationStat(__WRAPPED_CALL, __raw_size);

	#define	__NSTAT_WRAPDELETE(__WRAPPED_CALL, __SYSTEM)\
	if	(addr)\
	{\
		GS::Alloc::Header *h;\
		addr = GS::Alloc::GetAllocationStat(addr, h);\
		GS::Alloc::UpdateStatDelete(h->size, __SYSTEM);\
		__WRAPPED_CALL;\
	}

	#define	__NSTAT_ALLOC(__SIZE, __SYSTEM)		GS::Alloc::UpdateStatAlloc(__SIZE, __SYSTEM)
	#define	__NSTAT_DELETE(__SIZE, __SYSTEM)	GS::Alloc::UpdateStatDelete(__SIZE, __SYSTEM)

#else

	#define	__NSTAT_WRAPALLOC(__WRAPPED_CALL, __SYSTEM)		return __WRAPPED_CALL;
	#define	__NSTAT_WRAPDELETE(__WRAPPED_CALL, __SYSTEM)	__WRAPPED_CALL;

	#define	__NSTAT_ALLOC(__SIZE, __SYSTEM)
	#define	__NSTAT_DELETE(__SIZE, __SYSTEM)

#endif
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
#if	__PLATFORM_EMSCRIPTEN__

#define	NPLACEMENT_NEW(__SYSTEM)

#else

#define	NPLACEMENT_NEW(__SYSTEM)\
	void		*operator new (size_t size)\
	{	return MemAlloc##__SYSTEM::Alloc(size, GS::Alloc::__SYSTEM);	}\
	void		operator delete(void *addr)\
	{	MemAlloc##__SYSTEM::Delete(addr, GS::Alloc::__SYSTEM);	}\
	void		*operator new [] (size_t size)\
	{	return MemAlloc##__SYSTEM::Alloc(size, GS::Alloc::__SYSTEM);	}\
	void		operator delete [] (void *addr)\
	{	MemAlloc##__SYSTEM::Delete(addr, GS::Alloc::__SYSTEM);	}

#endif
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void	*_align_alloc(size_t, size_t);
void	_align_free(void *);

template <class T> T *_align_new(size_t align)
{	return ::new (_align_alloc(sizeof(T), align)) T();	}
template <class T, typename P0> T *_align_new(P0 &p0, size_t align)
{	return ::new (_align_alloc(sizeof(T), align)) T(p0);	}
template <class T> void _align_delete(T *p)
{
	p->~T();
	_align_free(p);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
template <class T> void _safe_delete(T *&p)
{
	delete p;
	p = 0;
}
template <class T> void _safe_delete_array(T *&p)
{
	delete [] p;
	p = 0;
}
//------------------------------------------------------------------------------


#endif	// __NALLOCATOR_INTERFACE__
