#ifndef __ARRTEMPL_INL__
#define __ARRTEMPL_INL__

template< class TYPE >
static inline void ConstructElements( TYPE* pElements, int nCount )
{
  memset( (void*)pElements, 0, nCount * sizeof( TYPE ) );
  for( ; nCount--; pElements++ ) *pElements = TYPE();
    //::new( (void*)pElements ) TYPE;
}

template< class TYPE >
inline void DestructElements( TYPE* pElements, int nCount )
{
  for (; nCount--; pElements++ )
    pElements->~TYPE();
}

template< class TYPE >
inline void CopyElements( TYPE* pDest, const TYPE* pSrc, int nCount )
{  
  for( ; nCount; nCount--, pDest++, pSrc++ )
    *pDest = *pSrc;
}

/////////////////////////////////////////////////////////////////////////////
// CArray<TYPE, ARG_TYPE>

template<class TYPE, class ARG_TYPE>
class CArray
{
public:
// Construction
  CArray();

// Attributes
  int GetSize() const;
  int GetUpperBound() const;
  void SetSize(int nNewSize, int nGrowBy = -1);

// Operations
  // Clean up
  void FreeExtra();
  void RemoveAll();

  // Accessing elements
  TYPE GetAt(int nIndex) const;
  void SetAt(int nIndex, ARG_TYPE newElement);
  TYPE& ElementAt(int nIndex);

  // Direct Access to the element data (may return NULL)
  const TYPE* GetData() const;
  TYPE* GetData();

  // Potentially growing the array
  void SetAtGrow(int nIndex, ARG_TYPE newElement);
  int Add(ARG_TYPE newElement);
  int Append(const CArray& src);
  void Copy(const CArray& src);
  const CArray& operator = ( const CArray& src );

  // overloaded operator helpers
  TYPE operator[](int nIndex) const;
  TYPE& operator[](int nIndex);

  // Operations that move elements around
  void InsertAt(int nIndex, ARG_TYPE newElement, int nCount = 1);
  void RemoveAt(int nIndex, int nCount = 1);
  void InsertAt(int nStartIndex, CArray* pNewArray);

// Implementation
protected:
  TYPE* m_pData;   // the actual array of data
  int m_nSize;     // # of elements (upperBound - 1)
  int m_nMaxSize;  // max allocated
  int m_nGrowBy;   // grow amount

public:
  ~CArray();
};

/////////////////////////////////////////////////////////////////////////////
// CArray<TYPE, ARG_TYPE> inline functions

template<class TYPE, class ARG_TYPE>
inline int CArray<TYPE, ARG_TYPE>::GetSize() const
  { return m_nSize; }
template<class TYPE, class ARG_TYPE>
inline int CArray<TYPE, ARG_TYPE>::GetUpperBound() const
  { return m_nSize-1; }
template<class TYPE, class ARG_TYPE>
inline void CArray<TYPE, ARG_TYPE>::RemoveAll()
  { SetSize( 0, -1 ); }
template<class TYPE, class ARG_TYPE>
inline TYPE CArray<TYPE, ARG_TYPE>::GetAt(int nIndex) const
  { return m_pData[nIndex];}
template<class TYPE, class ARG_TYPE>
inline void CArray<TYPE, ARG_TYPE>::SetAt(int nIndex, ARG_TYPE newElement)
  { m_pData[nIndex] = newElement; }
template<class TYPE, class ARG_TYPE>
inline TYPE& CArray<TYPE, ARG_TYPE>::ElementAt(int nIndex)
  { return m_pData[nIndex]; }
template<class TYPE, class ARG_TYPE>
inline const TYPE* CArray<TYPE, ARG_TYPE>::GetData() const
  { return (const TYPE*)m_pData; }
template<class TYPE, class ARG_TYPE>
inline TYPE* CArray<TYPE, ARG_TYPE>::GetData()
  { return (TYPE*)m_pData; }
template<class TYPE, class ARG_TYPE>
inline int CArray<TYPE, ARG_TYPE>::Add(ARG_TYPE newElement)
  { int nIndex = m_nSize;
    SetAtGrow( nIndex, newElement );
    return nIndex; }
template<class TYPE, class ARG_TYPE>
inline TYPE CArray<TYPE, ARG_TYPE>::operator[](int nIndex) const
  { return GetAt( nIndex ); }
template<class TYPE, class ARG_TYPE>
inline TYPE& CArray<TYPE, ARG_TYPE>::operator[](int nIndex)
  { return ElementAt( nIndex ); }
template<class TYPE, class ARG_TYPE>
inline const CArray<TYPE, ARG_TYPE>& CArray<TYPE, ARG_TYPE>::operator = ( const CArray& src )
  {  Copy( src ); return *this; }

/////////////////////////////////////////////////////////////////////////////
// CArray<TYPE, ARG_TYPE> out-of-line functions

template<class TYPE, class ARG_TYPE>
CArray<TYPE, ARG_TYPE>::CArray()
{
  m_pData = NULL;
  m_nSize = m_nMaxSize = m_nGrowBy = 0;
}

template<class TYPE, class ARG_TYPE>
CArray<TYPE, ARG_TYPE>::~CArray()
{
  RemoveAll();
}

template<class TYPE, class ARG_TYPE>
void CArray<TYPE, ARG_TYPE>::SetSize(int nNewSize, int nGrowBy)
{
  if( nGrowBy != -1 )
    m_nGrowBy = nGrowBy;
  if( nNewSize == 0 )
  {
    if( m_pData != NULL )
    {
      DestructElements( m_pData, m_nSize );
      delete [] (BYTE*)m_pData;
      m_pData = NULL;
    }
    m_nSize = m_nMaxSize = 0;
  }
  else if( m_pData == NULL )
  {
    m_pData = (TYPE*)new BYTE[nNewSize * sizeof( TYPE )];
    ConstructElements( m_pData, nNewSize );
    m_nSize = m_nMaxSize = nNewSize;
  }
  else if( nNewSize <= m_nMaxSize )
  {
    if( nNewSize > m_nSize )
      ConstructElements(&m_pData[m_nSize], nNewSize-m_nSize);
    else if( m_nSize > nNewSize )
      DestructElements(&m_pData[nNewSize], m_nSize-nNewSize);
    m_nSize = nNewSize;
  }
  else
  {
    int nGrowBy = m_nGrowBy;
    if( nGrowBy == 0 )
    {
      nGrowBy = m_nSize / 8;
      nGrowBy = (nGrowBy < 4) ? 4 : ((nGrowBy > 1024) ? 1024 : nGrowBy);
    }
    int nNewMax;
    if( nNewSize < m_nMaxSize + nGrowBy )
      nNewMax = m_nMaxSize + nGrowBy;
    else
      nNewMax = nNewSize;
    TYPE* pNewData = (TYPE*)new BYTE[nNewMax * sizeof( TYPE )];
    memcpy( pNewData, m_pData, m_nSize * sizeof( TYPE ) );

    ConstructElements( &pNewData[m_nSize], nNewSize-m_nSize );

    delete [] (BYTE*)m_pData;
    m_pData = pNewData;
    m_nSize = nNewSize;
    m_nMaxSize = nNewMax;
  }
}

template<class TYPE, class ARG_TYPE>
int CArray<TYPE, ARG_TYPE>::Append(const CArray& src)
{
  int nOldSize = m_nSize;
  SetSize( m_nSize + src.m_nSize );
  CopyElements( m_pData + nOldSize, src.m_pData, src.m_nSize );
  return nOldSize;
}

template<class TYPE, class ARG_TYPE>
void CArray<TYPE, ARG_TYPE>::Copy(const CArray& src)
{
  RemoveAll();
  SetSize( src.m_nSize );
  CopyElements( m_pData, src.m_pData, src.m_nSize );
}

template<class TYPE, class ARG_TYPE>
void CArray<TYPE, ARG_TYPE>::FreeExtra()
{
  if( m_nSize != m_nMaxSize )
  {
    TYPE* pNewData = NULL;
    if (m_nSize != 0)
    {
      pNewData = (TYPE*)new BYTE[m_nSize * sizeof( TYPE )];
      memcpy( pNewData, m_pData, m_nSize * sizeof( TYPE ) );
    }
    delete[] (BYTE*)m_pData;
    m_pData = pNewData;
    m_nMaxSize = m_nSize;
  }
}

template<class TYPE, class ARG_TYPE>
void CArray<TYPE, ARG_TYPE>::SetAtGrow(int nIndex, ARG_TYPE newElement)
{
  if (nIndex >= m_nSize)
    SetSize(nIndex + 1, -1);
  m_pData[nIndex] = newElement;
}

template<class TYPE, class ARG_TYPE>
void CArray<TYPE, ARG_TYPE>::InsertAt(int nIndex, ARG_TYPE newElement, int nCount /*=1*/)
{
  if (nIndex >= m_nSize)
    SetSize(nIndex + nCount, -1);
  else
  {
    int nOldSize = m_nSize;
    SetSize( m_nSize + nCount, -1 );
    memmove( &m_pData[nIndex + nCount], &m_pData[nIndex], ( nOldSize - nIndex) * sizeof( TYPE ) );
    ConstructElements( &m_pData[nIndex], nCount );
  }
  while( nCount-- )
    m_pData[nIndex++] = newElement;
}

template<class TYPE, class ARG_TYPE>
void CArray<TYPE, ARG_TYPE>::RemoveAt(int nIndex, int nCount)
{
  int nMoveCount = m_nSize - ( nIndex + nCount );
  DestructElements( &m_pData[nIndex], nCount );
  if( nMoveCount )
    memcpy( &m_pData[nIndex], &m_pData[nIndex + nCount], nMoveCount * sizeof( TYPE ) );
  m_nSize -= nCount;
}

template<class TYPE, class ARG_TYPE>
void CArray<TYPE, ARG_TYPE>::InsertAt( int nStartIndex, CArray* pNewArray )
{
  if( pNewArray->GetSize() > 0 )
  {
    InsertAt( nStartIndex, pNewArray->GetAt( 0 ), pNewArray->GetSize() );
    for( int i = 0; i < pNewArray->GetSize(); i++ )
      SetAt( nStartIndex + i, pNewArray->GetAt( i ) );
  }
}

#endif
