/* Copyright (c) 2002-2012 Croteam Ltd. 
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as published by
the Free Software Foundation


This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */


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

#include <Engine/Templates/StaticStackArray.h>
#include <Engine/Templates/StaticStackArray.cpp>
#include <Engine/Templates/StaticArray.cpp>

extern BOOL _bAllocationArrayParanoiaCheck;

/*
 * Default constructor.
 */
template<class Type>
inline CAllocationArray<Type>::CAllocationArray(void) : 
  CStaticArray<Type>(),
  aa_aiFreeElements()
{
  aa_ctAllocationStep = 256;
}

/*
 * Destructor.
 */
template<class Type>
inline CAllocationArray<Type>::~CAllocationArray(void) {
};

/* Destroy all objects, and reset the array to initial (empty) state. */
template<class Type>
inline void CAllocationArray<Type>::Clear(void) {
  // delete the objects themselves
  CStaticArray<Type>::Clear();
  // clear array of free indices
  aa_aiFreeElements.Clear();
}

  /*
 * Set how many elements to allocate when stack overflows.
 */
template<class Type>
inline void CAllocationArray<Type>::SetAllocationStep(INDEX ctStep) 
{
  ASSERT(ctStep>0);
  aa_ctAllocationStep = ctStep;
};

/*
 * Create a given number of objects.
 */
template<class Type>
inline void CAllocationArray<Type>::New(INDEX iCount) {
  // never call this!
  ASSERT(FALSE);
};

/*
 * Destroy all objects.
 */
template<class Type>
inline void CAllocationArray<Type>::Delete(void) {
  // never call this!
  ASSERT(FALSE);
}

/* Alocate a new object. */
template<class Type>
inline INDEX CAllocationArray<Type>::Allocate(void)
{
  // if there are no more free indices
  if (aa_aiFreeElements.Count()==0) {
    // remember old size
    INDEX ctOldSize = CStaticArray<Type>::Count();
    // expand the array by the allocation step
    this->Expand(ctOldSize+aa_ctAllocationStep);
    // create new free indices
    INDEX *piNewFree = aa_aiFreeElements.Push(aa_ctAllocationStep);
    // fill them up
    for(INDEX iNew=0; iNew<aa_ctAllocationStep; iNew++) {
      piNewFree[iNew] = ctOldSize+iNew;
    }
  }
  // pop one free index from the top of stack, and use that one
  return aa_aiFreeElements.Pop();
}
/* Free object with given index. */
template<class Type>
inline void CAllocationArray<Type>::Free(INDEX iToFree)
{
#ifndef NDEBUG
  // must be within pool limits
  ASSERT(iToFree>=0 && iToFree<CStaticArray<Type>::Count());
  // must not be free
  if (_bAllocationArrayParanoiaCheck) {
    ASSERT(IsAllocated(iToFree));
  }
#endif
  // push its index on top of the free stack
  aa_aiFreeElements.Push() = iToFree;
}

/* Free all objects, but keep pool space. */
template<class Type>
inline void CAllocationArray<Type>::FreeAll(void)
{
  // clear the free array
  aa_aiFreeElements.PopAll();
  // push as much free elements as there is pool space
  INDEX ctSize = CStaticArray<Type>::Count();
  INDEX *piNewFree = aa_aiFreeElements.Push(ctSize);
  // fill them up
  for(INDEX iNew=0; iNew<ctSize; iNew++) {
    piNewFree[iNew] = iNew;
  }
}

// check if an index is allocated (slow!)
template<class Type>
inline BOOL CAllocationArray<Type>::IsAllocated(INDEX i)
{
  // must be within pool limits
  ASSERT(i>=0 && i<CStaticArray<Type>::Count());
  // for each free index
  INDEX ctFree = aa_aiFreeElements.Count();
  for(INDEX iFree=0; iFree<ctFree; iFree++) {
    // if it is that one
    if (aa_aiFreeElements[iFree]==i) {
      // it is not allocated
      return FALSE;
    }
  }
  // if not found as free, it is allocated
  return TRUE;
}

/* Random access operator. */
// rcg10162001 wtf...I had to move this into the class definition itself.
//  I think it's an optimization bug; I didn't have this problem when I
//  didn't give GCC the "-O2" option.
#if 0
template<class Type>
inline Type &CAllocationArray<Type>::operator[](INDEX iObject)
{
#ifndef NDEBUG
  ASSERT(this!=NULL);
  // must be within pool limits
  ASSERT(iObject>=0 && iObject<CStaticArray<Type>::Count());
  // must not be free
  if (_bAllocationArrayParanoiaCheck) {
    ASSERT(IsAllocated(iObject));
  }
#endif
  return CStaticArray<Type>::operator[](iObject);
}
template<class Type>
inline const Type &CAllocationArray<Type>::operator[](INDEX iObject) const
{
#ifndef NDEBUG
  ASSERT(this!=NULL);
  // must be within pool limits
  ASSERT(iObject>=0 && iObject<CStaticArray<Type>::Count());
  // must not be free
  if (_bAllocationArrayParanoiaCheck) {
    ASSERT(IsAllocated(iObject));
  }
#endif
  return CStaticArray<Type>::operator[](iObject);
}
#endif  // 0

/* Get number of allocated objects in array. */
template<class Type>
INDEX CAllocationArray<Type>::Count(void) const
{
  ASSERT(this!=NULL);
  // it is pool size without the count of free elements
  return CStaticArray<Type>::Count()-aa_aiFreeElements.Count();
}

/* Get index of a object from it's pointer. */
template<class Type>
INDEX CAllocationArray<Type>::Index(Type *ptObject)
{
  ASSERT(this!=NULL);
  INDEX i = CStaticArray<Type>::Index(this->ptMember);
  ASSERT(IsAllocated(i));
  return i;
}

/* Assignment operator. */
template<class Type>
CAllocationArray<Type> &CAllocationArray<Type>::operator=(
  const CAllocationArray<Type> &aaOriginal)
{
  ASSERT(this!=NULL);
  (CStaticArray<Type>&)(*this) = (CStaticArray<Type>&)aaOriginal;
  aa_aiFreeElements = aaOriginal.aa_aiFreeElements;
  aa_ctAllocationStep = aaOriginal.aa_ctAllocationStep;
}


#endif  /* include-once check. */