#pragma once

namespace Mix{

	template<typename T>
	class Stack
	{
	private:
		UInt32 m_Pos;
		UInt32 m_Capacity;
		UInt32 m_ResizeStep;
		T* m_pBuffer;

#ifdef _DEBUG
		Mix::StringW m_DebugName;
#endif //_DEBUG

	public:
		Stack( void ) :
		m_Pos( 0 ),
		m_Capacity( 0 ),
		m_pBuffer( NULL )
		{
#ifdef _DEBUG
			m_DebugName = L"";
#endif //_DEBUG
		}

		Stack( UInt32 defCapacity, UInt32 resizeStep, const wchar_t* pDebugName = NULL ) :
		m_Pos( 0 ),
		m_Capacity( 0 ),
		m_pBuffer( NULL )
		{
#ifdef _DEBUG
			m_DebugName = L"";
#endif //_DEBUG

			Initialize( defCapacity, resizeStep, pDebugName );
		}

		~Stack( void )
		{
#ifdef _DEBUG
			MIX_LOG_DEV_INFO( L"Mix::Stack : Release : Capacity[%d]", m_Capacity );
#endif //_DEBUG

			MIX_LIB_DELETE_ARRAY( m_pBuffer );
		}

		void Initialize( UInt32 defCapacity, UInt32 resizeStep, const wchar_t* pDebugName = NULL )
		{
			MIX_ASSERT( defCapacity > 0 );
			MIX_ASSERT( resizeStep > 0 );

			MIX_LIB_DELETE_ARRAY( m_pBuffer );

			m_pBuffer = MIX_LIB_NEW T[defCapacity];
			if( m_pBuffer != NULL )
			{
				m_Capacity = defCapacity;
				m_ResizeStep = resizeStep;
			}

#ifdef _DEBUG
			m_DebugName = MIX_SAFE_NAME( pDebugName );

			MIX_LOG_INFO( L"Mix::Stack : Initialize : Name[%s] Capacity[%u] ResizeStep[%d]",
				m_DebugName.GetConstPtr(),
				m_Capacity,
				m_ResizeStep );
#endif //_DEBUG
		}

		T* Push( void )
		{
			MIX_ASSERT( m_pBuffer != NULL );

			if( m_Pos == m_Capacity )
			{
				if( Resize() == False )
				{
					return NULL;
				}
			}

			return &( m_pBuffer[m_Pos++] );
		}

		void Push( const T& value )
		{
			MIX_ASSERT( m_pBuffer != NULL );

			if( m_Pos == m_Capacity )
			{
				if( Resize() == False )
				{
					return;
				}
			}

			m_pBuffer[m_Pos] = value;
			m_Pos++;
		}

		T Pop( void )
		{
			MIX_ASSERT( m_pBuffer != NULL );
			MIX_ASSERT( m_Pos > 0 );

			return m_pBuffer[--m_Pos];
		}

		void Drain( void )
		{
			m_Pos = 0;
		}

		const T& GetCurrent( void ) const
		{
			MIX_ASSERT( m_Pos > 0 );

			return m_pBuffer[m_Pos - 1];
		}

		T& GetBottom( void ) const
		{
			MIX_ASSERT( m_Pos > 0 );

			return m_pBuffer[0];
		}

		UInt32 GetCount( void ) const
		{
			return m_Pos;
		}

		UInt32 GetCapacity( void )
		{
			return m_Capacity;
		}

	private:
		Boolean Resize( void )
		{
			MIX_ASSERT( m_Pos == m_Capacity );

			UInt32 nextCapacity = m_Capacity + m_ResizeStep;

			T* pNextBuffer = MIX_LIB_NEW T[nextCapacity];
#ifdef _DEBUG
			if( pNextBuffer == NULL )
			{
				MIX_LOG_ERROR( L"Mix::Stack : %s : Name[%s]",
					Mix::STR_OUTOFMEMORY,
					m_DebugName.GetConstPtr() );

				return False;
			}
#endif //_DEBUG
			::memcpy_s( pNextBuffer, sizeof( T ) * nextCapacity, m_pBuffer, sizeof( T ) * m_Capacity );

			MIX_LIB_DELETE_ARRAY( m_pBuffer );
			m_pBuffer = pNextBuffer;
			m_Capacity = nextCapacity;

#ifdef _DEBUG
			MIX_LOG_INFO( L"Mix::Stack : Resize : Name[%s] Capacity[%u]",
				m_DebugName.GetConstPtr(),
				m_Capacity );
#endif //_DEBUG

			return True;
		}
	};

	template<Mix::Memory::SECTION_TYPE ST, typename T>
	class StackT
	{
	private:
		UInt32 m_Pos;
		UInt32 m_Capacity;
		UInt32 m_ResizeStep;
		T* m_pBuffer;

#ifdef _DEBUG
		Mix::StringW m_DebugName;
#endif //_DEBUG

	public:
		StackT( void ) :
		m_Pos( 0 ),
		m_Capacity( 0 ),
		m_pBuffer( NULL )
		{
#ifdef _DEBUG
			m_DebugName = L"";
#endif //_DEBUG
		}

		StackT( UInt32 defCapacity, UInt32 resizeStep, const wchar_t* pDebugName = NULL ) :
		m_Pos( 0 ),
		m_Capacity( 0 ),
		m_pBuffer( NULL )
		{
#ifdef _DEBUG
			m_DebugName = L"";
#endif //_DEBUG

			Initialize( defCapacity, resizeStep, pDebugName );
		}

		~StackT( void )
		{
#ifdef _DEBUG
			MIX_LOG_DEV_INFO( L"Mix::StackT : Release : Capacity[%d]", m_Capacity );
#endif //_DEBUG

			MIX_LIB_DELETE_ARRAY_T( T, m_pBuffer );
		}

		void Initialize( UInt32 defCapacity, UInt32 resizeStep, const wchar_t* pDebugName = NULL )
		{
			MIX_ASSERT( defCapacity > 0 );
			MIX_ASSERT( resizeStep > 0 );

			MIX_LIB_DELETE_ARRAY_T( T, m_pBuffer );

			m_pBuffer = MIX_LIB_NEW_ARRAY_T( ST, T, defCapacity );
			if( m_pBuffer != NULL )
			{
				m_Capacity = defCapacity;
				m_ResizeStep = resizeStep;
			}

#ifdef _DEBUG
			m_DebugName = MIX_SAFE_NAME( pDebugName );

			MIX_LOG_INFO( L"Mix::StackT : Initialize : Name[%s] Capacity[%u] ResizeStep[%d]",
				m_DebugName.GetConstPtr(),
				m_Capacity,
				m_ResizeStep );
#endif //_DEBUG
		}

		T* Push( void )
		{
			MIX_ASSERT( m_pBuffer != NULL );

			if( m_Pos == m_Capacity )
			{
				if( Resize() == False )
				{
					return NULL;
				}
			}

			return &( m_pBuffer[m_Pos++] );
		}

		void Push( const T& value )
		{
			MIX_ASSERT( m_pBuffer != NULL );

			if( m_Pos == m_Capacity )
			{
				if( Resize() == False )
				{
					return;
				}
			}

			m_pBuffer[m_Pos] = value;
			m_Pos++;
		}

		T Pop( void )
		{
			MIX_ASSERT( m_pBuffer != NULL );
			MIX_ASSERT( m_Pos > 0 );

			return m_pBuffer[--m_Pos];
		}

		void Drain( void )
		{
			m_Pos = 0;
		}

		const T& GetCurrent( void ) const
		{
			MIX_ASSERT( m_Pos > 0 );

			return m_pBuffer[m_Pos - 1];
		}

		T& GetBottom( void ) const
		{
			MIX_ASSERT( m_Pos > 0 );

			return m_pBuffer[0];
		}

		UInt32 GetCount( void ) const
		{
			return m_Pos;
		}

		UInt32 GetCapacity( void )
		{
			return m_Capacity;
		}

	private:
		Boolean Resize( void )
		{
			MIX_ASSERT( m_Pos == m_Capacity );

			UInt32 nextCapacity = m_Capacity + m_ResizeStep;

			T* pNextBuffer = MIX_LIB_NEW_ARRAY_T( ST, T, nextCapacity );
#ifdef _DEBUG
			if( pNextBuffer == NULL )
			{
				MIX_LOG_ERROR( L"Mix::StackT : %s : Name[%s]",
					Mix::STR_OUTOFMEMORY,
					m_DebugName.GetConstPtr() );

				return False;
			}
#endif //_DEBUG
			::memcpy_s( pNextBuffer, sizeof( T ) * nextCapacity, m_pBuffer, sizeof( T ) * m_Capacity );

			MIX_LIB_DELETE_ARRAY_T( T, m_pBuffer );
			m_pBuffer = pNextBuffer;
			m_Capacity = nextCapacity;

#ifdef _DEBUG
			MIX_LOG_INFO( L"Mix::StackT : Resize : Name[%s] Capacity[%u]",
				m_DebugName.GetConstPtr(),
				m_Capacity );
#endif //_DEBUG

			return True;
		}
	};

}
