//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
// A growable array class that maintains a free list and keeps elements
// in the same location
//=============================================================================//

#ifndef UTLARRAY_H
#define UTLARRAY_H

#ifdef _WIN32
#pragma once
#endif

#include "tier0/platform.h"
#include "tier0/dbg.h"
#include "vstdlib/random.h"

#define FOR_EACH_ARRAY( vecName, iteratorName ) \
	for ( int iteratorName = 0; (vecName).IsUtlArray && iteratorName < (vecName).Count(); iteratorName++ )
#define FOR_EACH_ARRAY_BACK( vecName, iteratorName ) \
	for ( int iteratorName = (vecName).Count()-1; (vecName).IsUtlArray && iteratorName >= 0; iteratorName-- )

// utlarray derives from this so we can do the type check above
struct base_array_t
{
public:
	static const bool IsUtlArray = true; // Used to match this at compiletime 		
};

#if defined( GNUC ) && defined( DEBUG )
// gcc in debug doesn't optimize away the need for the storage of IsUtlArray so make one here
//  as this is in a shared header use SELECTANY to make it throw away the dupe symbols
const bool base_array_t::IsUtlArray SELECTANY;
#endif

//-----------------------------------------------------------------------------
template< class T, size_t MAX_SIZE >
class CUtlArray : public base_array_t
{
public:
	typedef T ElemType_t;
	typedef T* iterator;
	typedef const T* const_iterator;

	CUtlArray();
	CUtlArray( T* pMemory, size_t count );
	~CUtlArray();
	
	CUtlArray<T, MAX_SIZE>& operator=( const CUtlArray<T, MAX_SIZE> &other );
	CUtlArray( CUtlArray const& vec );

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

	// STL compatible member functions. These allow easier use of std::sort
	// and they are forward compatible with the C++ 11 range-based for loops.
	iterator begin();
	const_iterator begin() const;
	iterator end();
	const_iterator end() const;

	T* Base();
	const T* Base() const;

	// Returns the number of elements in the array, NumAllocated() is included for consistency with UtlVector
	int Count() const;
	int NumAllocated() const;

	// Is element index valid?
	bool IsValidIndex( int i ) const;
	static int InvalidIndex();

	void CopyArray( const T *pArray, size_t count );

	void Swap( CUtlArray< T, MAX_SIZE > &vec );
	
	// Finds an element (element needs operator== defined)
	int Find( const T& src ) const;
	void FillWithValue( const T& src );

	bool HasElement( const T& src ) const;

	// calls delete on each element in it.
	void DeleteElements();

	void Sort( int (__cdecl *pfnCompare)(const T *, const T *) );

protected:
	T m_Memory[ MAX_SIZE ];
};

//-----------------------------------------------------------------------------
// constructor, destructor
//-----------------------------------------------------------------------------
template< typename T, size_t MAX_SIZE >
inline CUtlArray<T, MAX_SIZE>::CUtlArray()
{
}

template< typename T, size_t MAX_SIZE >
inline CUtlArray<T, MAX_SIZE>::CUtlArray( T* pMemory, size_t count )
{
	CopyArray( pMemory, count );
}

template< typename T, size_t MAX_SIZE >
inline CUtlArray<T, MAX_SIZE>::~CUtlArray()
{
}

template< typename T, size_t MAX_SIZE >
inline CUtlArray<T, MAX_SIZE>& CUtlArray<T, MAX_SIZE>::operator=( const CUtlArray<T, MAX_SIZE> &other )
{
	if ( this != &other )
	{
		for ( size_t n = 0; n < MAX_SIZE; ++n )
		{
			m_Memory[n] = other.m_Memory[n];
		}
	}
	return *this;
}

template< typename T, size_t MAX_SIZE >
inline CUtlArray<T, MAX_SIZE>::CUtlArray( CUtlArray const& vec )
{
	for ( size_t n = 0; n < MAX_SIZE; ++n )
	{
		m_Memory[n] = vec.m_Memory[n];
	}
}

template< typename T, size_t MAX_SIZE >
typename CUtlArray<T, MAX_SIZE>::iterator CUtlArray<T, MAX_SIZE>::begin()
{ 
	return Base(); 
}

template< typename T, size_t MAX_SIZE >
typename CUtlArray<T, MAX_SIZE>::const_iterator CUtlArray<T, MAX_SIZE>::begin() const
{ 
	return Base(); 
}

template< typename T, size_t MAX_SIZE >
typename CUtlArray<T, MAX_SIZE>::iterator CUtlArray<T, MAX_SIZE>::end()
{ 
	return Base() + Count(); 
}

template< typename T, size_t MAX_SIZE >
typename CUtlArray<T, MAX_SIZE>::const_iterator CUtlArray<T, MAX_SIZE>::end() const
{ 
	return Base() + Count(); 
}

template< typename T, size_t MAX_SIZE >
inline T *CUtlArray<T, MAX_SIZE>::Base()								
{ 
	return &m_Memory[0]; 
}

template< typename T, size_t MAX_SIZE >
inline const T *CUtlArray<T, MAX_SIZE>::Base() const					
{ 
	return &m_Memory[0]; 
}

//-----------------------------------------------------------------------------
// element access
//-----------------------------------------------------------------------------
template< typename T, size_t MAX_SIZE >
inline T& CUtlArray<T, MAX_SIZE>::operator[]( int i )
{
	Assert( IsValidIndex( i ) );
	return m_Memory[ i ];
}

template< typename T, size_t MAX_SIZE >
inline const T& CUtlArray<T, MAX_SIZE>::operator[]( int i ) const
{
	Assert( IsValidIndex( i ) );
	return m_Memory[ i ];
}

template< typename T, size_t MAX_SIZE >
inline T& CUtlArray<T, MAX_SIZE>::Element( int i )
{
	Assert( IsValidIndex( i ) );
	return m_Memory[ i ];
}

template< typename T, size_t MAX_SIZE >
inline const T& CUtlArray<T, MAX_SIZE>::Element( int i ) const
{
	Assert( IsValidIndex( i ) );
	return m_Memory[ i ];
}

template< typename T, size_t MAX_SIZE >
inline T& CUtlArray<T, MAX_SIZE>::Random()
{
	Assert( MAX_SIZE > 0 );
	return m_Memory[ RandomInt( 0, MAX_SIZE - 1 ) ];
}

template< typename T, size_t MAX_SIZE >
inline const T& CUtlArray<T, MAX_SIZE>::Random() const
{
	Assert( MAX_SIZE > 0 );
	return m_Memory[ RandomInt( 0, MAX_SIZE - 1 ) ];
}


//-----------------------------------------------------------------------------
// Count
//-----------------------------------------------------------------------------
template< typename T, size_t MAX_SIZE >
inline int CUtlArray<T, MAX_SIZE>::Count() const
{
	return (int)MAX_SIZE;
}


//-----------------------------------------------------------------------------
template< typename T, size_t MAX_SIZE >
inline int CUtlArray<T, MAX_SIZE>::NumAllocated() const
{
	return (int)MAX_SIZE;
}

//-----------------------------------------------------------------------------
// Is element index valid?
//-----------------------------------------------------------------------------
template< typename T, size_t MAX_SIZE >
inline bool CUtlArray<T, MAX_SIZE>::IsValidIndex( int i ) const
{
	return (i >= 0) && (i < MAX_SIZE);
}
 

//-----------------------------------------------------------------------------
// Returns in invalid index
//-----------------------------------------------------------------------------
template< typename T, size_t MAX_SIZE >
inline int CUtlArray<T, MAX_SIZE>::InvalidIndex()
{
	return -1;
}


//-----------------------------------------------------------------------------
// Sorts the vector
//-----------------------------------------------------------------------------
template< typename T, size_t MAX_SIZE >
void CUtlArray<T, MAX_SIZE>::Sort( int (__cdecl *pfnCompare)(const T *, const T *) )
{
	typedef int (__cdecl *QSortCompareFunc_t)(const void *, const void *);
	if ( Count() <= 1 )
		return;

	qsort( Base(), Count(), sizeof(T), (QSortCompareFunc_t)(pfnCompare) );
}

template< typename T, size_t MAX_SIZE >
void CUtlArray<T, MAX_SIZE>::CopyArray( const T *pArray, size_t count )
{
	Assert( count < MAX_SIZE );

	for ( size_t n = 0; n < count; ++n )
	{
		m_Memory[n] = pArray[n];
	}
}

template< typename T, size_t MAX_SIZE >
void CUtlArray<T, MAX_SIZE>::Swap( CUtlArray< T, MAX_SIZE > &vec )
{
	for ( size_t n = 0; n < MAX_SIZE; ++n )
	{
		V_swap( m_Memory[n], vec.m_Memory[n] );
	}
}

//-----------------------------------------------------------------------------
// Finds an element (element needs operator== defined)
//-----------------------------------------------------------------------------
template< typename T, size_t MAX_SIZE >
int CUtlArray<T, MAX_SIZE>::Find( const T& src ) const
{
	for ( int i = 0; i < Count(); ++i )
	{
		if (Element(i) == src)
			return i;
	}
	return -1;
}

template< typename T, size_t MAX_SIZE >
void CUtlArray<T, MAX_SIZE>::FillWithValue( const T& src )
{
	for ( int i = 0; i < Count(); i++ )
	{
		Element(i) = src;
	}
}

template< typename T, size_t MAX_SIZE >
bool CUtlArray<T, MAX_SIZE>::HasElement( const T& src ) const
{
	return ( Find(src) >= 0 );
}

template< typename T, size_t MAX_SIZE >
inline void CUtlArray<T, MAX_SIZE>::DeleteElements()
{
	for( int i=0; i < MAX_SIZE; i++ )
	{
		delete Element(i);
	}
}

#endif // UTLARRAY_H