/* Copyright (c) 2002-2012 Croteam Ltd. All rights reserved. */

#ifndef SE_INCL_STATICARRAY_CPP
#define SE_INCL_STATICARRAY_CPP
#ifdef PRAGMA_ONCE
  #pragma once
#endif

#define FOREACHINSTATICARRAY(array, type, iter) \
  for(CStaticArrayIterator<type> iter(array); !iter.IsPastEnd(); iter.MoveToNext() )

#include <Engine/Base/Console.h>
#include <Engine/Templates/StaticArray.h>

/*
 * Default constructor.
 */
template<class Type>
inline CStaticArray<Type>::CStaticArray(void) {
  sa_Count=0;
  sa_Array=NULL;
}

/*
 * Destructor.
 */
template<class Type>
inline CStaticArray<Type>::~CStaticArray(void) {
  // if some objects were allocated
  if (sa_Count!=0) {
    // destroy them
    Delete();
  }
};

/* Random access operator. */
template<class Type>
inline void CStaticArray<Type>::operator=(const CStaticArray<Type> &arOriginal) {
  CopyArray(arOriginal);
}

template<class Type>
/* Destroy all objects, and reset the array to initial (empty) state. */
inline void CStaticArray<Type>::Clear(void) {
  if (sa_Count!=0) Delete(); 
}

/*
 * Create a given number of objects.
 */
template<class Type>
inline void CStaticArray<Type>::New(INDEX iCount) {
  ASSERT(this!=NULL && iCount>=0);
  // if no new members are needed in fact
  if (iCount==0) {
    // do nothing
    return;
  }
  //ASSERT(sa_Count==0 && sa_Array==NULL);
#ifndef NDEBUG
  if(!(sa_Count==0 && sa_Array==NULL)) {
    if(sa_Array == NULL) {
      CPrintF("CStaticArray array not set!\n");
    } else {
      CPrintF("CStaticArray new(%d) called while already holding %d elements!\n", iCount, sa_Count);
    }
  }
#endif
  sa_Count = iCount;
  sa_Array = new Type[iCount+1]; //(+1 for cache-prefetch opt)
};
/* Expand stack size but keep old objects. */
template<class Type>
inline void CStaticArray<Type>::Expand(INDEX iNewCount)
{
  ASSERT(this!=NULL && iNewCount>sa_Count);
  // if not already allocated
  if (sa_Count==0) {
    // just allocate
    New(iNewCount);
    return;
  // if already allocated
  } else {
    ASSERT(sa_Count!=0 && sa_Array!=NULL);
    // allocate new array with more space
    Type *ptNewArray = new Type[iNewCount+1]; //(+1 for cache-prefetch opt)
    // copy old objects
    for (INDEX iOld=0; iOld<sa_Count; iOld++) {
      ptNewArray[iOld] = sa_Array[iOld];
    }
    // free old array
    delete[] sa_Array;
    // remember the new array
    sa_Count = iNewCount;
    sa_Array = ptNewArray;
  }
}

/*
 * Destroy all objects.
 */
template<class Type>
inline void CStaticArray<Type>::Delete(void) {
  ASSERT(this!=NULL);
  ASSERT(sa_Count!=0 && sa_Array!=NULL);
  delete[] sa_Array;
  sa_Count = 0;
  sa_Array = NULL;
}

/*
 * Random access operator.
 */
template<class Type>
inline Type &CStaticArray<Type>::operator[](INDEX i) {
  ASSERT(this!=NULL);
  ASSERT(i>=0 && i<sa_Count);     // check bounds
  return sa_Array[i];
}
template<class Type>
inline const Type &CStaticArray<Type>::operator[](INDEX i) const {
  ASSERT(this!=NULL);
  ASSERT(i>=0 && i<sa_Count);     // check bounds
  return sa_Array[i];
}

/*
 * Get number of elements in array.
 */
template<class Type>
INDEX CStaticArray<Type>::Count(void) const {
  ASSERT(this!=NULL);
  return sa_Count;
}

/*
 * Get index of a member from it's pointer
 */
template<class Type>
INDEX CStaticArray<Type>::Index(Type *ptMember) {
  ASSERT(this!=NULL);
  INDEX i = ptMember-sa_Array;
  ASSERT(i>=0 && i<sa_Count);
  return i;
}

/*
 * Assignment operator.
 */
template<class Type>
/* Copy all elements of another array into this one. */
void CStaticArray<Type>::CopyArray(const CStaticArray<Type> &arOriginal)
{
  ASSERT(this!=NULL);
  ASSERT(&arOriginal!=NULL);
  ASSERT(this!=&arOriginal);

  // clear previous contents
  Clear();
  // get count of elements in original array
  INDEX ctOriginal = arOriginal.Count();
  // if the other array has no elements
  if (ctOriginal ==0) {
    return;
  }
  // create that much elements
  New(ctOriginal);
  // copy them all
  for (INDEX iNew=0; iNew<ctOriginal; iNew++) {
    sa_Array[iNew] = arOriginal[iNew];
  }
}

/* Move all elements of another array into this one. */
template<class Type>
void CStaticArray<Type>::MoveArray(CStaticArray<Type> &arOther)
{
  ASSERT(this!=NULL);
  ASSERT(&arOther!=NULL);
  ASSERT(this!=&arOther);

  // clear previous contents
  Clear();
  // if the other array has no elements
  if (arOther.Count()==0) {
    // no assignment
    return;
  }
  // move data from the other array into this one and clear the other one
  sa_Count = arOther.sa_Count;
  sa_Array = arOther.sa_Array;
  arOther.sa_Count = 0;
  arOther.sa_Array = NULL;
}

/////////////////////////////////////////////////////////////////////
// CStaticArrayIterator

/*
 * Template class for iterating static array.
 */
template<class Type>
class CStaticArrayIterator {
private:
  INDEX sai_Index;          // index of current element
  CStaticArray<Type> &sai_Array;   // reference to array
public:
  /* Constructor for given array. */
  inline CStaticArrayIterator(CStaticArray<Type> &sa);
  /* Destructor. */
  inline ~CStaticArrayIterator(void);

  /* Move to next object. */
  inline void MoveToNext(void);
  /* Check if finished. */
  inline BOOL IsPastEnd(void);
  /* Get current element. */
  Type &Current(void) { return sai_Array[sai_Index]; }
  Type &operator*(void) { return sai_Array[sai_Index]; }
  operator Type *(void) { return &sai_Array[sai_Index]; }
  Type *operator->(void) { return &sai_Array[sai_Index]; }
};

/*
 * Constructor for given array.
 */
template<class Type>
inline CStaticArrayIterator<Type>::CStaticArrayIterator(CStaticArray<Type> &sa) : sai_Array(sa) {
  sai_Index = 0;
}

/*
 * Destructor.
 */
template<class Type>
inline CStaticArrayIterator<Type>::~CStaticArrayIterator(void) {
  sai_Index = -1;
}

/*
 * Move to next object.
 */
template<class Type>
inline void CStaticArrayIterator<Type>::MoveToNext(void) {
  ASSERT(this!=NULL);
  sai_Index++;
}

/*
 * Check if finished.
 */
template<class Type>
inline BOOL CStaticArrayIterator<Type>::IsPastEnd(void) {
  ASSERT(this!=NULL);
  return sai_Index>=sai_Array.sa_Count;
}


#endif  /* include-once check. */