/* 
* PROJECT: NyARToolkitCPP
* --------------------------------------------------------------------------------
*
* The NyARToolkitCPP is C++ version NyARToolkit class library.
* Copyright (C)2008-2009 Ryo Iizuka
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* 
* 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, see <http://www.gnu.org/licenses/>.
* 
* For further information please contact.
*	http://nyatla.jp/nyatoolkit/
*	<airmail(at)ebony.plala.or.jp> or <nyatla(at)nyatla.jp>
* 
*/
#pragma once
#include "NyArray.h"
#include <exception>
using namespace std;
namespace NyARToolkitCPP
{
	template <class T> class NyObjectStack
	{
	private:
		const static int ARRAY_APPEND_STEP=64;
	protected:
		NyArray<T*>* _items;
		int _length;
	private:
		int _allocated_size;
	private:
		//コピーコンストラクタ削除
		NyObjectStack(const NyObjectStack& );
		NyObjectStack& operator=(const NyObjectStack&);
	public:
		NyObjectStack(int i_size)
		{
			// ポインタだけははじめに確保しておく
			this->_items =new NyArray<T*>(i_size);

			// アロケート済サイズと、使用中個数をリセット
			this->_allocated_size = 0;
			this->_length = 0;
			return;
		}
		~NyObjectStack(void)
		{
			for(int i=0;i<this->_allocated_size;i++){
				delete *(this->_items->item+i);
			}
			delete this->_items;
			return;
		}
		T* prePush()
		{
			// 必要に応じてアロケート
			if (this->_length >= this->_allocated_size) {
				// 要求されたインデクスは範囲外
				if (this->_length >= this->_items->length) {
					throw exception();
				}
				// 追加アロケート範囲を計算
				int range = this->_length + ARRAY_APPEND_STEP;
				if (range >= this->_items->length) {
					range = this->_items->length;
				}
				// アロケート
				this->onReservRequest(this->_allocated_size, range, *this->_items);
				this->_allocated_size = range;
			}
			// 使用領域を+1して、予約した領域を返す。
			T* ret = this->_items->item[this->_length];
			this->_length++;
			return ret;
		}
		T* pop()
		{
			if(this->_length<1){
				throw exception();
			}
			this->_length--;
			return this->getItem(this->_length);
		}
		void pops(int i_count)
		{
			if(this->_length<i_count){
				throw exception();
			}
			this->_length-=i_count;
			return;
		}
		const NyArray<T*>& getArray() const
		{
			return *(this->_items);
		}
		T* getItem(int i_index) const
		{
			return *(this->_items->item+i_index);
		}
		int getLength() const
		{
			return this->_length;
		}
		void clear()
		{
			this->_length = 0;
		}
	private:
		void onReservRequest(int i_start, int i_end, NyArray<T*>& i_buffer)
		{
			for (int i = i_start; i < i_end; i++) {
				i_buffer.item[i] = new T();
			}
			return;
		}
	public:
		 void reserv(int i_number_of_item)
		{
			// 必要に応じてアロケート
			if (i_number_of_item >= this->_allocated_size) {
				// 要求されたインデクスは範囲外
				if (i_number_of_item >= this->_items->length) {
					throw exception();
				}
				// 追加アロケート範囲を計算
				int range = i_number_of_item+ARRAY_APPEND_STEP;
				if (range >= this->_items->length) {
					range = this->_items->length;
				}
				// アロケート
				this->onReservRequest(this->_allocated_size, range, *this->_items);
				this->_allocated_size = range;
			}
			//見かけ上の配列サイズを指定
			this->_length=i_number_of_item;
			return;
		}
	};
}
