/**
 * 回呼函數: fnname (type: TType, arg: TArg): void
 */
interface ActionCallback<TType, TArg> {
	(type: TType, arg: TArg): void;
}

interface Struct<TType, TArg> {
	callback: ActionCallback<TType, TArg>;
	target: any;
	type: TType;
	once?: boolean;
}

export class ActionWithType2<TType, TArg> {
	private _queue: Struct<TType, TArg>[] = [];

	/**
	 * 監聽事件
	 * @param callback 回呼函數: fnname (type: TType, arg: TArg): void
	 * @param bindTarget 回呼時this綁定的對象
	 */
	AddCallback(type: TType, callback: ActionCallback<TType, TArg>, bindTarget?: any) {
		let q = <Struct<TType, TArg>>{
			callback: callback,
			target: bindTarget,
			type: type
		};
		this._queue.push(q);
	}

	/**
	 * 監聽事件 (一次性)
	 * @param callback 回呼函數: fnname (type: TType, arg: TArg): void
	 * @param bindTarget 回呼時this綁定的對象
	 */
	AddCallbackOnce(type: TType, callback: ActionCallback<TType, TArg>, bindTarget?: any) {
		let q = <Struct<TType, TArg>>{
			callback: callback,
			target: bindTarget,
			type: type,
			once: true
		};
		this._queue.push(q);
	}

	/**
	 * 移除事件
	 * @param callback
	 */
	RemoveByCallback(callback: ActionCallback<TType, TArg>) {
		let index = this._queue.length;
		if (index > 0) {
			while (index--) {
				let q = this._queue[index];
				if (!q.callback || q.callback === callback) {
					q.callback = undefined;
					this._queue.splice(index, 1);
				}
			}
		}
	}

	/**
	 * 移除事件
	 * @param bindTarget 回呼時this綁定的對象
	 */
	RemoveByBindTarget(bindTarget: any) {
		let index = this._queue.length;
		if (index > 0) {
			while (index--) {
				let q = this._queue[index];
				if (!q.callback || q.target === bindTarget) {
					q.callback = undefined;
					this._queue.splice(index, 1);
				}
			}
		}
	}

	/**
	 * 移除事件
	 * @param type 事件類型
	 */
	RemoveByType(type: TType) {
		let index = this._queue.length;
		if (index > 0) {
			while (index--) {
				let q = this._queue[index];
				if (!q.callback || q.type === type) {
					q.callback = undefined;
					this._queue.splice(index, 1);
				}
			}
		}
	}

	/**
	 * 移除事件
	 * @param type 事件類型
	 * @param callback
	 */
	RemoveCallback(type: TType, callback: ActionCallback<TType, TArg>) {
		let index = this._queue.length;
		if (index > 0) {
			while (index--) {
				let q = this._queue[index];
				if (!q.callback || (q.type === type && q.callback === callback)) {
					q.callback = undefined;
					this._queue.splice(index, 1);
				}
			}
		}
	}

	/**
	 * 移除全部事件
	 */
	RemoveAllCallbacks() {
		this._queue.forEach(q => q.callback = undefined);
		this._queue.length = 0;
	}

	/**
	 * 發送事件
	 * @param type 事件類型
	 * @param arg 參數
	 */
	DispatchCallback(type: TType, arg: TArg) {
		let index = this._queue.length;
		if (index > 0) {
			let cleanRemoved = false;
			this._queue.slice().forEach(q => {
				if (!q.callback) {
					cleanRemoved = true;
					return;
				}
				if (q.type !== type) return;

				if (q.target) {
					q.callback.call(q.target, type, arg);
				} else {
					q.callback(type, arg);
				}

				if (q.once) {
					q.callback = undefined;
					cleanRemoved = true;
				}
			});

			if (cleanRemoved) {
				index = this._queue.length;
				if (index > 0) {
					while (index--) {
						let q = this._queue[index];
						if (!q.callback) {
							this._queue.splice(index, 1);
						}
					}
				}
			}
		}
	}
}
