#pragma once

#define ONLY_MSPACES 1
#define USE_LOCKS 1
#define MIX_USE_WIN_MALLOC

#ifndef MIX_USE_WIN_MALLOC
	#include "malloc.c"
#endif //MIX_USE_WIN_MALLOC

namespace Mix{ namespace Memory{

	class System
	{
	public:
		enum SECTION_TYPE
		{
			SECTION_LIBRARY		= 0,
			SECTION_USER		= 1,
			SECTION_MISC		= 2,
#ifdef _DEBUG
			SECTION_DEBUG		= 3,
			SECTION_TYPE_MAX	= 4,
#else //_DEBUG
			SECTION_TYPE_MAX	= 3,
#endif //_DEBUG
		};

#ifndef MIX_USE_WIN_MALLOC
	private:
		static mspace g_Spaces[System::SECTION_TYPE_MAX];

	public:
		System( void );
		~System( void );

	private:
		static void Initialize( void );
		static void Terminate( void );
#endif //MIX_USE_WIN_MALLOC

	public:
		static inline void* Allocate( System::SECTION_TYPE secType, UIntT size, const wchar_t* srcFile, Int32 srcLine )
		{
			static const UInt16 alignment = sizeof( size_t );
			MIX_ASSERT( ( alignment >= 4 ) && ( alignment <= 65535 ) );

			void* temp;
			UInt8* ret;

#ifdef MIX_USE_WIN_MALLOC
			temp = ::_aligned_malloc( size + alignment, alignment );
#else //MIX_USE_DLMALLOC
			temp = mspace_malloc( System::g_Spaces[secType], size + alignment );
#endif //MIX_USE_DLMALLOC
			if( temp != NULL )
			{
				ret = reinterpret_cast<UInt8*>( temp ) + alignment;

				*( UInt16* )( ret - 2 ) = alignment;
				*( UInt16* )( ret - 4 ) = secType;
			}
			else
			{
				ret = NULL;
			}

			return ret;
		}

		static inline void* AllocateAligned( System::SECTION_TYPE secType, UIntT size, UIntT alignment, const wchar_t* srcFile, Int32 srcLine )
		{
			MIX_ASSERT( ( alignment >= 4 ) && ( alignment <= 65535 ) );

			void* temp;
			UInt8* ret;

#ifdef MIX_USE_WIN_MALLOC
			temp = ::_aligned_malloc( size + alignment, alignment );
#else //MIX_USE_DLMALLOC
			temp = mspace_memalign( System::g_Spaces[secType], alignment, size + alignment );
#endif //MIX_USE_DLMALLOC
			if( temp != NULL )
			{
				ret = reinterpret_cast<UInt8*>( temp ) + alignment;

				*( UInt16* )( ret - 2 ) = MIX_UIT_TO_UI16( alignment );
				*( UInt16* )( ret - 4 ) = secType;
			}
			else
			{
				ret = NULL;
			}

			return ret;
		}

		static inline void* Reallocate( void* ptr, UIntT size )
		{
			MIX_ASSERT( ptr != NULL );
			MIX_ASSERT( size != 0 );

			UInt8* addr = reinterpret_cast<UInt8*>( ptr );
			UInt16 alignment = *( UInt16* )( addr - 2 );
			UInt16 secType = *( UInt16* )( addr - 4 );

			void* temp;
			UInt8* ret;

			addr -= alignment;

#ifdef MIX_USE_WIN_MALLOC
			temp = ::_aligned_realloc( addr, size + alignment, alignment );
#else //MIX_USE_DLMALLOC
			temp = mspace_realloc( System::g_Spaces[secType], addr, size + alignment );
#endif //MIX_USE_DLMALLOC
			if( temp != NULL )
			{
				ret = reinterpret_cast<UInt8*>( temp ) + alignment;

				*( UInt16* )( ret - 2 ) = alignment;
				*( UInt16* )( ret - 4 ) = secType;
			}
			else
			{
				ret = NULL;
			}

			return ret;
		}

		static inline void Free( void* ptr )
		{
			UInt8* addr = reinterpret_cast<UInt8*>( ptr );
			UInt16 alignment = *( UInt16* )( addr - 2 );
			UInt16 secType = *( UInt16* )( addr - 4 );

			addr -= alignment;

#ifdef MIX_USE_WIN_MALLOC
			::_aligned_free( addr );
#else //MIX_USE_DLMALLOC
			mspace_free( System::g_Spaces[secType], addr );
#endif //MIX_USE_DLMALLOC
		}
	};

}}
