/* 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_DYNAMICSTACKARRAY_CPP
#define SE_INCL_DYNAMICSTACKARRAY_CPP
#ifdef PRAGMA_ONCE
  #pragma once
#endif

#include <Engine/Templates/DynamicStackArray.h>
#include <Engine/Templates/DynamicArray.cpp>

/*
 * Default constructor.
 */
template<class Type>
inline CDynamicStackArray<Type>::CDynamicStackArray(void) : CDynamicArray<Type>() {
  da_ctUsed=0;
  da_ctAllocationStep = 256;
  // lock the array on construction
  CDynamicArray<Type>::Lock();
}

/*
 * Destructor.
 */
template<class Type>
inline CDynamicStackArray<Type>::~CDynamicStackArray(void) {
  // lock the array on destruction
  CDynamicArray<Type>::Unlock();
};

/* Destroy all objects, and reset the array to initial (empty) state. */
template<class Type>
inline void CDynamicStackArray<Type>::Clear(void) {
  CDynamicArray<Type>::Clear(); da_ctUsed = 0; 
}

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

/*
 * Add new object(s) on top of stack.
 */
template<class Type>
inline Type &CDynamicStackArray<Type>::Push(void) {
  // if there are no free elements in the array
  if (CDynamicArray<Type>::Count()-da_ctUsed<1) {
    // alocate a new block
    CDynamicArray<Type>::New(da_ctAllocationStep);
  }
  // get the new element
  da_ctUsed++;
  ASSERT(da_ctUsed <= CDynamicArray<Type>::Count());
  return CDynamicArray<Type>::operator[](da_ctUsed-1);
}
template<class Type>
inline Type *CDynamicStackArray<Type>::Push(INDEX ct) {
  // if there are no free elements in the array
  while(CDynamicArray<Type>::Count()-da_ctUsed<ct) {
    // alocate a new block
    CDynamicArray<Type>::New(da_ctAllocationStep);
  }
  // get new elements
  da_ctUsed+=ct;
  ASSERT(da_ctUsed <= CDynamicArray<Type>::Count());
  return &CDynamicArray<Type>::operator[](da_ctUsed-ct);
}

/*
 * Remove all objects from stack, but keep stack space.
 */
template<class Type>
inline void CDynamicStackArray<Type>::PopAll(void) {
  // if there is only one block allocated
  if ( this->da_BlocksList.IsEmpty()
    || &this->da_BlocksList.Head()==&this->da_BlocksList.Tail()) {
    // just clear the counter
    da_ctUsed = 0;

  // if there is more than one block allocated
  } else {
    // remember how much was allocated, rounded up to allocation step
    INDEX ctUsedBefore = CDynamicArray<Type>::Count();
    // free all memory
    CDynamicArray<Type>::Clear();
    // allocate one big block
    CDynamicArray<Type>::New(ctUsedBefore);
    da_ctUsed = 0;
  }
}

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

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

/*
 * Get index of a member from it's pointer
 */
template<class Type>
INDEX CDynamicStackArray<Type>::Index(Type *ptMember) {
  ASSERT(this!=NULL);
  INDEX i = CDynamicArray<Type>::Index(ptMember);
  ASSERTMSG(i<da_ctUsed, "CDynamicStackArray<>::Index(): Not a member of this array!");
  return i;
}

/*
 * Get array of pointers to elements (used for sorting elements by sorting pointers).
 */
template<class Type>
Type **CDynamicStackArray<Type>::GetArrayOfPointers(void)
{
  return this->da_Pointers;
}

/*
 * Assignment operator.
 */
template<class Type>
CDynamicStackArray<Type> &CDynamicStackArray<Type>::operator=(CDynamicStackArray<Type> &arOriginal)
{
  ASSERT(this!=NULL);
  ASSERT(&arOriginal!=NULL);
  ASSERT(this!=&arOriginal);

  // copy stack arrays
  CDynamicArray<Type>::operator=(arOriginal);
  // copy used count
  da_ctUsed = arOriginal.da_ctUsed;

  return *this;
}


#endif  /* include-once check. */