import { NumberEx } from "@/utils/Number/NumberEx";
import { CoroutineV2 } from "../CatanEngine/CoroutineV2/CoroutineV2";
import { ActionWithType } from "../CatanEngine/CSharp/System/ActionWithType";

class TimerEvent extends ActionWithType<number, any> { }

/**
 * 計時器(使用CoroutineV2)
 */
export class Timer {

	//#region private

	/** 訊息資料 */
	private static Group: Map<any, TimerDataClass> = new Map<any, TimerDataClass>();

	//#endregion

	//#region static

	/**
	 * 啟動計時
	 * @param {number} time 計時(seconds)
	 * @param {Function} callback Function
	 * @param {any} type (選填) 可以識別的東西
	 * @param {any} bindTarget (選填) 回呼時this綁定的對象
	 * @example
	 * Timer.Start(1, () => { console.log(`example`); });
	 * Timer.Start(1, () => { console.log(`example`); }, "example");
	 * Timer.Start(1, () => { console.log(`example`); }, "example", this);
	 */
	public static Start(time: number, callback: Function, bindTarget?: any, type?: any): void {
		let self: typeof Timer = this;
		let thisType: any = type;
		if (!type) {
			thisType = callback;
		}
		if (Timer.Group.has(thisType)) {
			console.error(`Timer Start Error Timer.Group.has(${thisType})`);
			return;
		}
		let timerData: TimerDataClass = new TimerDataClass(thisType, time, callback, bindTarget);
		Timer.Group.set(thisType, timerData);
		let CoroutineFN: () => IterableIterator<any> = function* (): IterableIterator<any> {
			yield CoroutineV2.WaitTime(time).Start(bindTarget);
			if (Timer.Group.has(thisType)) {
				self._callback(timerData.Type, timerData.Callback, timerData.BindTarget);
			}
		};
		CoroutineV2.Single(CoroutineFN()).Start(bindTarget);
	}

	/**
	 * 刪除計時 By Target
	 * @param {any} target target
	 * @example
	 * Timer.ClearByTarget(this);
	 */
	public static ClearByTarget(target: any): void {
		let timerDataGroup: TimerDataClass[] = [];
		Timer.Group.forEach(timerData => {
			if (timerData.BindTarget === target) {
				timerDataGroup.push(timerData);
			}
		});
		if (timerDataGroup.length === 0) {
			console.warn(`Timer Clear Error Timer.Group.has not target`);
			return;
		}
		for (let i: number = 0; i < timerDataGroup.length; i++) {
			let timerData: TimerDataClass = timerDataGroup[i];
			let type: any = timerData.Type;
			Timer.Group.delete(type);
			timerData = null;
		}
		CoroutineV2.StopCoroutinesBy(target);
	}

	/**
	 * 刪除計時 By Type
	 * @param PS 還是會吃效能在倒數 只是時間到不會執行
	 * @param {any} type type
	 * @example
	 * Timer.ClearByType("example");
	 */
	public static ClearByType(type: any): void {
		let timerDataGroup: TimerDataClass[] = [];
		Timer.Group.forEach(timerData => {
			if (timerData.Type === type) {
				timerDataGroup.push(timerData);
			}
		});
		if (timerDataGroup.length === 0) {
			console.warn(`Timer Clear Error Timer.Group.has not type`);
			return;
		}
		for (let i: number = 0; i < timerDataGroup.length; i++) {
			let timerData: TimerDataClass = timerDataGroup[i];
			let type: any = timerData.Type;
			Timer.Group.delete(type);
		}
	}

	/**
	 * 結束計時時callback
	 * @param {Function} callback Function
	 */
	private static _callback(type: any, callback: Function, bindTarget: any): void {
		if (Timer.Group.has(type)) {
			Timer.Group.delete(type);
		}
		if (bindTarget) {
			callback.bind(bindTarget)();
		} else {
			callback();
		}
	}

	/**
	 * 定時事件(時間用 updateTime秒跑一次fn)
	 * @param startNum 起始index
	 * @param endNum 結束index
	 * @param updateTime 事件刷新間隔
	 * @param callbackfn 事件
	 * @example
	 * let startNum: number = 10;
	 * let endNum: number = 0;
	 * let updateTime: number = 1;
	 * yield CoroutineV2.Single(Timer.Timing(
	 *      startNum,
	 *      endNum,
	 *      updateTime,
	 *      (x: number) => {
	 *          console.log(`x: ${x}`);
	 *      }
	 *  )).Start(this);
	 */
	public static *Timing(startNum: number, endNum: number, updateTime: number, callbackfn: Function): IterableIterator<any> {
		let isIncrease: boolean = endNum >= startNum;
		let totalCount: number = Math.abs(endNum - startNum) + 1;
		let nowCount: number = NumberEx.divide(totalCount, updateTime);
		let diff: number = NumberEx.divide(totalCount, nowCount) * (isIncrease ? 1 : -1);
		let tempScore: number = startNum;
		callbackfn(startNum);
		while (true) {
			if (endNum !== tempScore) {
				yield CoroutineV2.WaitTime(updateTime);
				tempScore += diff;
				// 遞增
				if (isIncrease && tempScore > endNum) {
					tempScore = endNum;
				}
				// 遞減
				if (!isIncrease && tempScore < endNum) {
					tempScore = endNum;
				}
				callbackfn(Math.floor(tempScore));
			} else {
				break;
			}
		}
	}

	//#endregion
}


//#region Class

/** Timer資料 */
export class TimerDataClass {
	/** Type */
	public Type: any = null;

	/** Time */
	public Time: number = null;

	/** Callback */
	public Callback: Function = null;

	/** BindTarget */
	public BindTarget?: any = null;

	constructor(type: any, time: number, callback: Function, bindTarget?: any) {
		this.Type = type;
		this.Time = time;
		this.Callback = callback;
		this.BindTarget = bindTarget;
	}
}

// //#endregion