#ifndef __ARRAY_H__
#define __ARRAY_H__

class CPtrArray
{
public:
// Granularity

	void SetGranularity( int nGrowBy ) { if(nGrowBy >= 0) m_nGrowBy = nGrowBy;}
	
// Construction
  CPtrArray();

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

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

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

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

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

  // Potentially growing the array
  void SetAtGrow(int nIndex, void* newElement);
  int Add(void* newElement);

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

// Implementation
protected:
  void** 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:
  ~CPtrArray();
};

inline int CPtrArray::GetSize() { return m_nSize; }
inline int CPtrArray::GetUpperBound() { return m_nSize-1; }
inline void CPtrArray::RemoveAll() { SetSize(0); }
inline void* CPtrArray::GetAt(int nIndex) const { if(nIndex < 0 || nIndex >= m_nSize) return NULL; return m_pData[nIndex]; }
inline void CPtrArray::SetAt(int nIndex, void* newElement) { if(nIndex < 0 || nIndex >= m_nSize) return; m_pData[nIndex] = newElement; }
inline int CPtrArray::Add(void* newElement) { int nIndex = m_nSize; SetAtGrow(nIndex, newElement); return nIndex; }
inline void*& CPtrArray::ElementAt(int nIndex){if(nIndex < 0 || nIndex >= m_nSize){void ** res = NULL; return *res;} return m_pData[nIndex]; }
inline const void** CPtrArray::GetData() const { return (const void**)m_pData; }
inline void** CPtrArray::GetData()  { return (void**)m_pData; }
inline void* CPtrArray::operator[](int nIndex) const { return GetAt(nIndex); }
inline void*& CPtrArray::operator[](int nIndex) { return ElementAt(nIndex); }


/////////////////////////////////////////////////////////////////////////////
// CTypedPtrArray<TYPE>

template<class TYPE>
class CTypedPtrArray : public CPtrArray
{
public:
  // Accessing elements
  TYPE GetAt(int nIndex) const
    { return (TYPE)CPtrArray::GetAt(nIndex); }
  TYPE& ElementAt(int nIndex)
    { return (TYPE&)CPtrArray::ElementAt(nIndex); }

  // overloaded operator helpers
  TYPE operator[](int nIndex) const
    { return (TYPE)CPtrArray::operator[](nIndex); }
  TYPE& operator[](int nIndex)
    { return (TYPE&)CPtrArray::operator[](nIndex); }
  // Deleting Elements
  void DeleteAt(int nIndex);
  void DeleteAll();
};

// Deleting Elements
template<class TYPE>
void CTypedPtrArray<TYPE>::DeleteAt(int nIndex)
{
  TYPE p;
  p = GetAt(nIndex);
  RemoveAt(nIndex);
  if(p) delete p;
}


template<class TYPE>
void CTypedPtrArray<TYPE>::DeleteAll()
{
  TYPE p;
  for(int i = GetUpperBound(); i>=0; i--) 
    if(p = GetAt(i)) delete p;
  RemoveAll();
}


/////////////////////////////////////////////////////////////////////////////
// CSTPArray<TYPE> > class Sorted Typed Pointer Array
// Created byCatalin Cocos
template<class TYPE>
class CSTPArray : public CTypedPtrArray<TYPE*>
{
  static CRITICAL_SECTION m_CriticalSection;
  static int m_iNumObjects;
  static int m_qsort_Key;
  int m_iKey;
  static int SCompare(TYPE**,TYPE**);
public:
  // Constructor
  CSTPArray(int iKey = 0);
  // Destructor
  ~CSTPArray();
  // Sorting elements
  void QSort(int iKey = 0);
  // Adding elements
  int SAdd(TYPE* newElement, BOOL & NotAdded, BOOL AdmitDuplicates = TRUE);
  // Searching elements
  int Index(TYPE* newElement, int *p_iidx = NULL);
  int UpperBound(int idx);
  int LowerBound(int idx);
};

// the static members
template<class TYPE>
int CSTPArray<TYPE>::m_iNumObjects = 0;
template<class TYPE>
int CSTPArray<TYPE>::m_qsort_Key = 0;
template<class TYPE>
CRITICAL_SECTION CSTPArray<TYPE>::m_CriticalSection;


// Constructor
template<class TYPE>
CSTPArray<TYPE>::CSTPArray(int iKey)
{
  m_iKey = iKey;
  if(!m_iNumObjects++)
    InitializeCriticalSection(&m_CriticalSection);
}

// Destructor
template<class TYPE>
CSTPArray<TYPE>::~CSTPArray()
{
  if(!--m_iNumObjects)
    DeleteCriticalSection(&m_CriticalSection);
}

// sorting the array
template<class TYPE>
int CSTPArray<TYPE>::SCompare(TYPE** a,TYPE** b)
{
  return TYPE::Compare(*a,*b,m_qsort_Key);
}

template<class TYPE>
void CSTPArray<TYPE>::QSort(int iKey)
{
  EnterCriticalSection(&m_CriticalSection);
  m_qsort_Key = m_iKey = iKey;
  qsort(GetData(),GetSize(),sizeof(TYPE*),(int (*)(const void *,const void *))SCompare);
  LeaveCriticalSection(&m_CriticalSection);
}

// Adding elements

template<class TYPE>
int CSTPArray<TYPE>::SAdd(TYPE* newElement, BOOL& NotAdded, BOOL AdmitDuplicates )
{
  int iidx, idx = Index( newElement, &iidx );
  NotAdded = idx>=0 && ( !AdmitDuplicates || (newElement == GetAt(idx)) );
  return (NotAdded? idx: InsertAt(iidx, newElement, 1),iidx );
}

// Searching elements
template<class TYPE>
int CSTPArray<TYPE>::Index(TYPE* newElement, int *p_iidx)
{
  if(p_iidx) *p_iidx = 0;
  if(!newElement) return -1;
  int hidx = GetUpperBound(), lidx = 0, cidx, val;
  if(hidx<0) return hidx;
  while(hidx > lidx)
  {
	  val = TYPE::Compare(newElement,GetAt(cidx = (hidx+lidx)/2),m_iKey);
	  if( !val ) hidx = lidx = cidx;
	  else if( val<0 ) hidx = cidx;
	  else lidx = cidx+1;
  }
  if(p_iidx) *p_iidx = lidx+1;
  val = TYPE::Compare(newElement,GetAt(lidx),m_iKey);
  if( !val )
  {
  	  if(p_iidx) *p_iidx = lidx+1;
      return lidx;
  }
  else if( val<0 )
  {
	  if(p_iidx) (*p_iidx)--;
  }
  return -1;
}

// finding the bounds of equivalence intervals (upper & lower)
template<class TYPE>
int CSTPArray<TYPE>::UpperBound(int idx)
{
  int Midx = GetUpperBound(), lidx, hidx, cidx; // the upper limit
  if(idx<0 || idx>=Midx ) return Midx;          // the item is out of bounds or the last in the array
  TYPE* pCrt = GetAt(lidx = hidx = idx);        // this is the target
  // if the last element of the array is <=> with the target, return its index (no use for further search)
  if(!TYPE::Compare(pCrt,GetAt(Midx),m_iKey)) return Midx;
  // find the interval bound (kinda' confusing method, isn't it?)
  for(hidx ++ ;!TYPE::Compare(pCrt,GetAt(hidx),m_iKey); lidx = hidx, hidx = min(Midx,idx+(hidx-idx)*2));
  hidx--;
  while(hidx > lidx)
    if(TYPE::Compare(pCrt,GetAt(cidx = (hidx+lidx+1)/2),m_iKey) < 0 ) hidx = cidx-1;
    else lidx = cidx;
  return lidx;
}

template<class TYPE>
int CSTPArray<TYPE>::LowerBound(int idx)
{
  int lidx, hidx, cidx;             
  if(idx<=0 || idx>GetUpperBound()) return 0;   // the item is out of bounds or the last in the array
  TYPE* pCrt = GetAt(lidx = hidx = idx);        // this is the target
  // if the first element of the array is <=> with the target, return its index (no use for further search)
  if(!TYPE::Compare(pCrt,GetAt(0),m_iKey)) return 0;
  // find the interval bound
  for(lidx--; !TYPE::Compare(pCrt,GetAt(lidx),m_iKey); hidx = lidx, lidx = max(0,idx-(idx-lidx)*2));
  lidx++;
  while(hidx > lidx)
    if(TYPE::Compare(pCrt,GetAt(cidx = (hidx+lidx)/2),m_iKey)>0) lidx = cidx+1;
	else hidx = cidx;
  return lidx;
}

#include "arrtempl.inl" // the CArray class

#endif
