//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
// A stack based on a growable array
//=============================================================================//

#ifndef UTLSTACK_H
#define UTLSTACK_H

#include <assert.h>
#include <string.h>
#include "utlmemory.h"


//-----------------------------------------------------------------------------
// The CUtlStack class:
// A growable stack class which doubles in size by default.
// It will always keep all elements consecutive in memory, and may move the
// elements around in memory (via a realloc) when elements are pushed or
// popped. Clients should therefore refer to the elements of the stack
// by index (they should *never* maintain pointers to elements in the stack).
//-----------------------------------------------------------------------------

template< class T, class M = CUtlMemory< T > > 
class CUtlStack
{
public:
	// constructor, destructor
	CUtlStack( int growSize = 0, int initSize = 0 );
	~CUtlStack();

	void CopyFrom( const CUtlStack<T, M> &from );

	// element access
	T& operator[]( int i );
	T const& operator[]( int i ) const;
	T& Element( int i );
	T const& Element( int i ) const;

	// Gets the base address (can change when adding elements!)
	T* Base();
	T const* Base() const;

	// Looks at the stack top
	T& Top();
	T const& Top() const;

	// Size
	int Count() const;

	// Is element index valid?
	bool IsIdxValid( int i ) const;

	// Adds an element, uses default constructor
	int Push();

	// Adds an element, uses copy constructor
	int Push( T const& src );

	// Pops the stack
	void Pop();
	void Pop( T& oldTop );
	void PopMultiple( int num );

	// Makes sure we have enough memory allocated to store a requested # of elements
	void EnsureCapacity( int num );

	// Clears the stack, no deallocation
	void Clear();

	// Memory deallocation
	void Purge();

private:
	// Grows the stack allocation
	void GrowStack();

	// For easier access to the elements through the debugger
	void ResetDbgInfo();

	M m_Memory;
	int m_Size;

	// For easier access to the elements through the debugger
	T* m_pElements;
};


//-----------------------------------------------------------------------------
// For easier access to the elements through the debugger
//-----------------------------------------------------------------------------

template< class T, class M >
inline void CUtlStack<T,M>::ResetDbgInfo()
{
	m_pElements = m_Memory.Base();
}

//-----------------------------------------------------------------------------
// constructor, destructor
//-----------------------------------------------------------------------------

template< class T, class M >
CUtlStack<T,M>::CUtlStack( int growSize, int initSize )	: 
	m_Memory(growSize, initSize), m_Size(0)
{
	ResetDbgInfo();
}

template< class T, class M >
CUtlStack<T,M>::~CUtlStack()
{
	Purge();
}


//-----------------------------------------------------------------------------
// copy into
//-----------------------------------------------------------------------------

template< class T, class M >
void CUtlStack<T,M>::CopyFrom( const CUtlStack<T, M> &from )
{
	Purge();
	EnsureCapacity( from.Count() );
	for ( int i = 0; i < from.Count(); i++ )
	{
		Push( from[i] );
	}
}

//-----------------------------------------------------------------------------
// element access
//-----------------------------------------------------------------------------

template< class T, class M >
inline T& CUtlStack<T,M>::operator[]( int i )
{
	assert( IsIdxValid(i) );
	return m_Memory[i];
}

template< class T, class M >
inline T const& CUtlStack<T,M>::operator[]( int i ) const
{
	assert( IsIdxValid(i) );
	return m_Memory[i];
}

template< class T, class M >
inline T& CUtlStack<T,M>::Element( int i )
{
	assert( IsIdxValid(i) );
	return m_Memory[i];
}

template< class T, class M >
inline T const& CUtlStack<T,M>::Element( int i ) const
{
	assert( IsIdxValid(i) );
	return m_Memory[i];
}


//-----------------------------------------------------------------------------
// Gets the base address (can change when adding elements!)
//-----------------------------------------------------------------------------

template< class T, class M >
inline T* CUtlStack<T,M>::Base()
{
	return m_Memory.Base();
}

template< class T, class M >
inline T const* CUtlStack<T,M>::Base() const
{
	return m_Memory.Base();
}

//-----------------------------------------------------------------------------
// Returns the top of the stack
//-----------------------------------------------------------------------------

template< class T, class M >
inline T& CUtlStack<T,M>::Top()
{
	assert( m_Size > 0 );
	return Element(m_Size-1);
}

template< class T, class M >
inline T const& CUtlStack<T,M>::Top() const
{
	assert( m_Size > 0 );
	return Element(m_Size-1);
}

//-----------------------------------------------------------------------------
// Size
//-----------------------------------------------------------------------------

template< class T, class M >
inline int CUtlStack<T,M>::Count() const
{
	return m_Size;
}


//-----------------------------------------------------------------------------
// Is element index valid?
//-----------------------------------------------------------------------------

template< class T, class M >
inline bool CUtlStack<T,M>::IsIdxValid( int i ) const
{
	return (i >= 0) && (i < m_Size);
}
 
//-----------------------------------------------------------------------------
// Grows the stack
//-----------------------------------------------------------------------------

template< class T, class M >
void CUtlStack<T,M>::GrowStack()
{
	if (m_Size >= m_Memory.NumAllocated())
		m_Memory.Grow();

	++m_Size;

	ResetDbgInfo();
}

//-----------------------------------------------------------------------------
// Makes sure we have enough memory allocated to store a requested # of elements
//-----------------------------------------------------------------------------

template< class T, class M >
void CUtlStack<T,M>::EnsureCapacity( int num )
{
	m_Memory.EnsureCapacity(num);
	ResetDbgInfo();
}


//-----------------------------------------------------------------------------
// Adds an element, uses default constructor
//-----------------------------------------------------------------------------

template< class T, class M >
int CUtlStack<T,M>::Push()
{
	GrowStack();
	Construct( &Element(m_Size-1) );
	return m_Size - 1;
}

//-----------------------------------------------------------------------------
// Adds an element, uses copy constructor
//-----------------------------------------------------------------------------

template< class T, class M >
int CUtlStack<T,M>::Push( T const& src )
{
	GrowStack();
	CopyConstruct( &Element(m_Size-1), src );
	return m_Size - 1;
}


//-----------------------------------------------------------------------------
// Pops the stack
//-----------------------------------------------------------------------------

template< class T, class M >
void CUtlStack<T,M>::Pop()
{
	assert( m_Size > 0 );
	Destruct( &Element(m_Size-1) );
	--m_Size;
}

template< class T, class M >
void CUtlStack<T,M>::Pop( T& oldTop )
{
	assert( m_Size > 0 );
	oldTop = Top();
	Pop();
}

template< class T, class M >
void CUtlStack<T,M>::PopMultiple( int num )
{
	assert( m_Size >= num );
	for ( int i = 0; i < num; ++i )
		Destruct( &Element( m_Size - i - 1 ) );
	m_Size -= num;
}


//-----------------------------------------------------------------------------
// Element removal
//-----------------------------------------------------------------------------

template< class T, class M >
void CUtlStack<T,M>::Clear()
{
	for (int i = m_Size; --i >= 0; )
		Destruct(&Element(i));

	m_Size = 0;
}


//-----------------------------------------------------------------------------
// Memory deallocation
//-----------------------------------------------------------------------------

template< class T, class M >
void CUtlStack<T,M>::Purge()
{
	Clear();
	m_Memory.Purge( );
	ResetDbgInfo();
}

#endif // UTLSTACK_H