module ytl.objectpool;

///	オブジェクトを事前に生成して貯めておく仕組み。
/**

オブジェクトプールは、結局のところ有限スタックとして実装される。
すなわち、サイズが事前に決まっているスタックであり、未使用オブジェクトを
このスタック上にpushして(積んで)いき、未使用オブジェクトをもらうには、
このスタックからpopして(降ろして)いくことにより、プールという仕組みを
実現することが出来る。

<PRE>
	alias objectPool!(Star) StarPool;
のようにして、
	//	☆オブジェクトを事前に生成しておく。
	StarPool pool = new StarPool;
	pool.setMax(500);
	foreach(inout Star s;pool) s = new Star;

プールからもらうときは、
	//	プールからオブジェクトをひとつもらう
	Star s = pool.pop();
	if (s) {
		s.reset(info,x,y);
		controller.addTask(s,0);
	}

プールに返すときは、
	if (time > 50){
		// 自殺する
		pool.push(this); // このオブジェクトをプールに戻す
		return 1;
	}
のようにする。

</PRE>
*/
template objectPool(T) {
///	objectPool本体
class objectPool {
	///	poolするオブジェクトの最大数を設定する
	/**
		事前に設定すること。
		設定すると、以前保持していたオブジェクトは
		解放してしまうので注意。
	*/
	void setMax(int n) {
		release();
		stack = new T[n];
	}

	void release() {
		if (stack) {
			//	stackの内容も巡回して明示的にdeleteするべきか?
			delete stack;
			stack = null;
		}
		sp = 0;
	}

	///	foreachに対応させるためのもの
	int opApply(int delegate(inout T) dg)
	{
		if (!stack) return 0;	//	ダメやん

		int result = 0;
		foreach(inout T t;stack) {
			result = dg(t);
			if (result)	break;
		}
		return result;
	}

	///	オブジェクトをひとつもらう
	/**
		未使用のオブジェクトがなければ、nullが返る。
	*/
	T pop(){
		if (!stack) return T.init;
		if (sp==stack.length) return T.init;
		return stack[sp++];
	}

	///	オブジェクトをひとつ返す
	/**
		最初setMaxで確保して、ここからpopで取得した分以外の
		オブジェクトを返してはならない。
	*/
	void push(T t){
		if (!stack) return ;
		if (sp==0) return ; // errorであるべきか..
		stack[--sp] = t;
	}

	/// 未使用のオブジェクト数を返す
	int getRest() { return stack.length - sp; }

	///	オブジェクトをひとつ返す
	/**
		オブジェクトに空きがない場合は、一番最後にpushForcedで返された
		オブジェクトを返す。(pushForcedと対にして使う)<BR>

		パーティクル等で一番古いオブジェクトを再利用したいときに
		これを使うと良い。
	*/
	T	popForced(){
		if (!stack) return T.init; // これは仕方ない
		if (sp==stack.length) {
			T t = stack[0];
			for(int i=0;i<sp-1;++i)
				stack[i] = stack[i+1];
			stack[sp-1] = t; // 一番最後に参照されたオブジェクトなのでここ。
			return t;
		}
		return stack[sp++];
	}

	///	オブジェクトをプールに戻す
	/**
		popForcedと対にして使えるように、戻すときに
		順序づけを行なって戻す。
	*/
	void pushForced(T t){
	/*
		Tが、stack[0]から現在のスタック位置までの間にあるはずなので
		それを削除して詰めていく。
	*/
		if (!stack) return ; // うひゃー。どうなっとるんじゃ
		for(int i=0;i<sp;++i){
			if (stack[i] == t){ // みっけ！
				//	間を詰めて
				for(int j=i;j<sp-1;++j) {
					stack[j] = stack[j+1];
				}
				//	現在のスタックのところに戻す
				stack[--sp] = t; return;
			}
		}
		//	死にオブジェクト突っ込まれたと思われ。
		assert(0);
	}

	//---- 以下、必要ならば使うヨロシ

	///	内部で使用しているspポインタそのまま返す
	int getSP() { return sp; }

	///	内部で使用しているspポインタにそのまま設定する
	void setSP(int sp_) { sp = sp_; }

	///	内部で使用しているスタックをそのまま返す
	T[] getStack() { return stack; }

private:
	T[] stack;
	int sp; // stack pointer
}}
