#include "StdInc.h"
#include "array.h"      

#include "LIMITS.H"
#define SIZE_T_MAX  UINT_MAX            /* max size for a size_t */

/////////////////////////////////////////////////////////////////////////////

CPtrArray::CPtrArray()
{
  m_pData = NULL;
  m_nSize = m_nMaxSize = m_nGrowBy = 0;
}

CPtrArray::~CPtrArray()
{
  delete [] (BYTE*)m_pData;
}

void CPtrArray::SetSize(int nNewSize, int nGrowBy /* = -1 */)
{
  if(nNewSize < 0)
    return;

  if (nGrowBy != -1)
    m_nGrowBy = nGrowBy;  // set new size

  if (nNewSize == 0)
  {
    // shrink to nothing
    delete [] (BYTE*)m_pData;
    m_pData = NULL;
    m_nSize = m_nMaxSize = 0;
  }
  else if (m_pData == NULL)
  {
    // create one with exact size
#ifdef SIZE_T_MAX
    if((long)nNewSize * sizeof(void*) > SIZE_T_MAX)
      return;  // overflow
#endif
    m_pData = (void**) new BYTE[nNewSize * sizeof(void*)];

    memset(m_pData, 0, nNewSize * sizeof(void*));  // zero fill

    m_nSize = m_nMaxSize = nNewSize;
  }
  else if (nNewSize <= m_nMaxSize)
  {
    // it fits
    if (nNewSize > m_nSize)
    {
      // initialize the new elements

      memset(&m_pData[m_nSize], 0, (nNewSize-m_nSize) * sizeof(void*));

    }

    m_nSize = nNewSize;
  }
  else
  {
    // Otherwise grow array
    int nNewMax;
    if (nNewSize < m_nMaxSize + m_nGrowBy)
      nNewMax = m_nMaxSize + m_nGrowBy;  // granularity
    else
      nNewMax = nNewSize;  // no slush

#ifdef SIZE_T_MAX
    if((long)nNewMax * sizeof(void*) > SIZE_T_MAX)
      return;  // overflow
#endif
    void** pNewData = (void**) new BYTE[nNewMax * sizeof(void*)];

    // copy new data from old
    memcpy(pNewData, m_pData, m_nSize * sizeof(void*));

    // construct remaining elements
    if(nNewSize <= m_nSize)
      return;

    memset(&pNewData[m_nSize], 0, (nNewSize-m_nSize) * sizeof(void*));


    // get rid of old stuff (note: no destructors called)
    delete [] (BYTE*)m_pData;
    m_pData = pNewData;
    m_nSize = nNewSize;
    m_nMaxSize = nNewMax;
  }
}

void CPtrArray::FreeExtra()
{
  if (m_nSize != m_nMaxSize)
  {
    // shrink to desired size
#ifdef SIZE_T_MAX
    if((long)m_nSize * sizeof(void*) > SIZE_T_MAX)
      return;  // overflow
#endif
    void** pNewData = NULL;
    if (m_nSize != 0)
    {
      pNewData = (void**) new BYTE[m_nSize * sizeof(void*)];
      // copy new data from old
      memcpy(pNewData, m_pData, m_nSize * sizeof(void*));
    }

    // get rid of old stuff (note: no destructors called)
    delete [] (BYTE*)m_pData;
    m_pData = pNewData;
    m_nMaxSize = m_nSize;
  }
}

/////////////////////////////////////////////////////////////////////////////

void CPtrArray::SetAtGrow(int nIndex, void* newElement)
{
  if(nIndex < 0)
    return;

  if (nIndex >= m_nSize)
    SetSize(nIndex+1);
  m_pData[nIndex] = newElement;
}

void CPtrArray::InsertAt(int nIndex, void* newElement, int nCount /*=1*/)
{
  if(nIndex < 0)
    return;    // will expand to meet need
  if(nCount <= 0)
    return;     // zero or negative size not allowed

  if (nIndex >= m_nSize)
  {
    // adding after the end of the array
    SetSize(nIndex + nCount);  // grow so nIndex is valid
  }
  else
  {
    // inserting in the middle of the array
    int nOldSize = m_nSize;
    SetSize(m_nSize + nCount);  // grow it to new size
    // shift old data up to fill gap
    memmove(&m_pData[nIndex+nCount], &m_pData[nIndex],
      (nOldSize-nIndex) * sizeof(void*));

    // re-init slots we copied from

    memset(&m_pData[nIndex], 0, nCount * sizeof(void*));

  }

  // insert new value in the gap
  if(nIndex + nCount > m_nSize)
    return;
  while (nCount--)
    m_pData[nIndex++] = newElement;
}

void CPtrArray::RemoveAt(int nIndex, int nCount /* = 1 */)
{
  if(nIndex < 0)
    return;
  if(nCount < 0)
    return;
  if(nIndex + nCount > m_nSize)
    return;

  // just remove a range
  int nMoveCount = m_nSize - (nIndex + nCount);

  if (nMoveCount)
    memcpy(&m_pData[nIndex], &m_pData[nIndex + nCount],
      nMoveCount * sizeof(void*));
  m_nSize -= nCount;
}

void CPtrArray::InsertAt(int nStartIndex, CPtrArray* pNewArray)
{
  if(pNewArray == NULL)
    return;
  if(nStartIndex < 0)
    return;

  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));
  }
}




