///<reference path="all.ts"/>
module jg {
	/**
	 * ハンドラ用インターフェース
	 */
	export interface TriggerHandler {
		/** ハンドラ所有者 */
		owner:any;
		/** コールバック */
		handler:Function;
	}

	/**
	 * jgame.jsのイベント機構を管理するクラス。
	 * TypeScriptに合ったイベント機構として、thisを指定可能なイベントハンドラを用いるために考案された。
	 * 1イベントに付き1つのTriggerを作る事を想定しているため、例えばDOMであるkeyDown、keyUpのイベントを作るなら、このようにする。
	 * this.keyDown = new jg.Trigger();
	 * this.keyUp = new jg.Trigger();
	 * 
	 * イベントハンドラの指定はhandleで行い、イベントの発火はfireで行う。
	 * イベントハンドラの削除はremove, removeAll, removeAllByHandler, destroyの4種が用意されており、匿名メソッドにおけるハンドラ削除をやりやすくしている。
	 * 
	 * Genericsにするとよりタイプセーフになるが、jgame.js開発開始時点ではGenericsが存在しなかったため導入されていない。将来のGenericsは検討中。
	 */
	export class Trigger {
		/** このTriggerに登録された全ハンドラ */
		handlers:TriggerHandler[];

		/**
		 * コンストラクタ
		 */
		constructor() {
			this.handlers = []
		}

		/**
		 * 所定の位置にハンドラを挿入する。ハンドラ順序が処理に影響する場合に指定する
		 * @param index 挿入位置。添え字は0から
		 * @param owner ハンドラ所有者。イベント発火時には、この値がthisとなる
		 * @param handler ハンドラのコールバック関数
		 */
		handleInsert(index:number, owner:any, handler?:Function) {
			if (! handler)
				this.handlers.splice(index, 0, {owner:window, handler:owner});
			else
				this.handlers.splice(index, 0, {owner:owner, handler:handler});
		}

		/**
		 * ハンドラを追加する
		 * @param owner ハンドラ所有者。イベント発火時には、この値がthisとなる
		 * @param handler ハンドラのコールバック関数
		 */
		handle(owner:any, handler?:Function) {
			if (! handler)
				this.handlers.push({owner:window, handler:owner});
			else
				this.handlers.push({owner:owner, handler:handler});
		}

		/**
		 * すべてのハンドラを削除する
		 */
		destroy() {
			this.handlers = [];
		}

		/**
		 * 指定された所有者のハンドラをすべて削除する
		 * @param owner 削除対象のハンドラ所有者
		 */
		removeAll(owner:any) {
			var ret = [];
			var tmp;
			while (tmp = this.handlers.shift())
				if (tmp.owner != owner)
					ret.push(tmp);

			this.handlers = ret;
		}

		/**
		 * 指定されたコールバック関数のハンドラをすべて削除する
		 * @param handler 削除対象のコールバック関数
		 */
		removeAllByHandler(handler:Function) {
			var ret = [];
			var tmp;
			while (tmp = this.handlers.shift())
				if (tmp.handler != handler)
					ret.push(tmp);

			this.handlers = ret;
		}

		/**
		 * ハンドラを削除する
		 * @param owner 削除対象のハンドラ所有者
		 * @param handler 削除対象のコールバック関数
		 */
		remove(owner:any, handler?:Function) {
			var ret = [];
			var tmp;
			if (! handler) {
				handler = owner;
				owner = window;
			}
			while (tmp = this.handlers.shift())
				if (tmp.handler != handler || tmp.owner != owner)
					ret.push(tmp);

			this.handlers = ret;
		}

		/**
		 * イベントを発火する
		 * @param param イベントパラメータ
		 */
		fire(param?:any) {
			if (this.handlers.length == 0)
				return;

			var handlers = this.handlers.concat();	//clone
			for (var i=0; i<handlers.length; i++)
				handlers[i].handler.call(handlers[i].owner, param);
		}

		/**
		 * 非セーフティな形でイベントを発火する。
		 * このメソッドの利用は極力控えるべき。ハンドラ内でイベントハンドラの追加や削除が行われると、すべてのハンドラに処理が伝達されないため。
		 * ハンドラ内で削除されないことが保障されている場合や、複数処理されてもかまわないような場合にのみ利用する。
		 * @param param イベントパラメータ
		 */
		fastFire(param?:any) {
			for (var i=0; i<this.handlers.length; i++)
				this.handlers[i].handler.call(this.handlers[i].owner, param);
		}
	}
}