
import { CoroutineV2 } from "@/Engine/CatanEngine/CoroutineV2/CoroutineV2";
import { RandomEx } from "./RandomEx";

export module NumberEx {
	/**
	 * 數字滾動
	 * @param startNum 開始
	 * @param endNum 結束
	 * @param callbackfn 結束回呼
	 * @param toInt 是否只顯示整數(預設FALSE)
	 */
	export function* ChangeScore(startNum: number, endNum: number, callbackfn: (num: number) => void, sec: number, toInt: boolean = true) {
		let fps = 30;
		let waitTime = 0.03;
		let changeRate = sec * fps;
		changeRate = changeRate - 1 <= 0 ? changeRate : changeRate - 1;
		changeRate = 1 / changeRate;
		let diff = endNum - startNum;
		let isIncrease = endNum >= startNum;
		let tempScore = startNum;
		let counter = 0;
		while (true) {
			if (endNum != tempScore) {
				tempScore += diff * changeRate;
				// 遞增
				if (isIncrease && tempScore > endNum) {
					tempScore = endNum;
				}
				// 遞減
				if (!isIncrease && tempScore < endNum) {
					tempScore = endNum;
				}
				if (toInt) {
					callbackfn(tempScore.ExToInt());
				} else {
					callbackfn(tempScore);
				}
				counter++;
				yield CoroutineV2.WaitTime(waitTime);
			}
			else {
				callbackfn(endNum);
				break;
			}
		}
	}

	/**
	 * 數字跳動(時間內循環EX:1~4=>1.2.3.4.1.2.3.4.1.2.3.4...)
	 * @param minNum 起始數字
	 * @param maxNum 最終數字
	 * @param callbackfn callbackfn
	 * @param sec 時間
	 */
	export function* BeatScore(minNum: number, maxNum: number, endNum: number, callbackfn: (num: number) => void, sec: number): IterableIterator<any> {
		let fps: number = 13;
		let waitTime: number = 0.07;
		let changeRate: number = sec * fps; // -1為了讓changeRate數字能混亂點
		changeRate = changeRate - 1 <= 0 ? changeRate : changeRate - 1;
		changeRate = 1 / changeRate;
		let diff: number = maxNum - minNum;
		let isIncrease: boolean = maxNum >= minNum;
		let tempScore: number = minNum;
		let counter: number = 0;
		let randomRate: number = 0;
		let lastNum: number = minNum;
		let nowNum: number = minNum;
		while (true) {
			if (maxNum !== tempScore) {
				if (counter % 2 === 0) {
					if (isIncrease) {
						randomRate = RandomEx.GetFloat(0, diff * changeRate).ExToNumFloorDecimal(2);
					} else {
						randomRate = RandomEx.GetFloat(0, -diff * changeRate).ExToNumFloorDecimal(2);
					}
				} else {
					randomRate = -randomRate;
				}

				tempScore += diff * changeRate + randomRate;
				// 遞增
				if (isIncrease && tempScore > maxNum) {
					tempScore = maxNum;
				}
				// 遞減
				if (!isIncrease && tempScore < maxNum) {
					tempScore = maxNum;
				}
				while (nowNum === lastNum) {
					nowNum = RandomEx.GetInt(minNum, maxNum + 1);
				}
				lastNum = nowNum;
				callbackfn(nowNum);
				// yield null;
				counter++;
				yield CoroutineV2.WaitTime(waitTime);
			} else {
				callbackfn(endNum);
				break;
			}
		}
	}

	/**是否进行边界检查 */
	let _boundaryCheckingState = false;

	/**
	 * 检测数字是否越界，如果越界给出提示
	 * @param {*number} num 输入数
	 */
	function checkBoundary(num: number) {
		if (_boundaryCheckingState) {
			if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) {
				console.warn(`${num} is beyond boundary when transfer to integer, the results may not be accurate`);
			}
		}
	}

	/**
	 * 精确乘法
	 */
	export function times(num1: number, num2: number, ...others: number[]): number {
		if (others.length > 0) {
			return times(times(num1, num2), others[0], ...others.slice(1));
		}
		const num1Changed = num1.Float2Fixed();
		const num2Changed = num2.Float2Fixed();
		const baseNum = num1.DigitLength() + num2.DigitLength();
		const leftValue = num1Changed * num2Changed;

		checkBoundary(leftValue);

		return leftValue / Math.pow(10, baseNum);
	}

	/**
	 * 精确加法
	 */
	export function plus(num1: number, num2: number, ...others: number[]): number {
		if (others.length > 0) {
			return plus(plus(num1, num2), others[0], ...others.slice(1));
		}
		const baseNum = Math.pow(10, Math.max(num1.DigitLength(), num2.DigitLength()));
		return (times(num1, baseNum) + times(num2, baseNum)) / baseNum;
	}

	/**
	 * 精确减法
	 */
	export function minus(num1: number, num2: number, ...others: number[]): number {
		if (others.length > 0) {
			return minus(minus(num1, num2), others[0], ...others.slice(1));
		}
		const baseNum = Math.pow(10, Math.max(num1.DigitLength(), num2.DigitLength()));
		return (times(num1, baseNum) - times(num2, baseNum)) / baseNum;
	}

	/**
	 * 精确除法
	 */
	export function divide(num1: number, num2: number, ...others: number[]): number {
		if (others.length > 0) {
			return divide(divide(num1, num2), others[0], ...others.slice(1));
		}
		const num1Changed = num1.Float2Fixed();
		const num2Changed = num2.Float2Fixed();
		checkBoundary(num1Changed);
		checkBoundary(num2Changed);
		return times((num1Changed / num2Changed), Math.pow(10, num2.DigitLength() - num1.DigitLength()));
	}

	/**
	 * 四舍五入
	 */
	export function round(num: number, ratio: number): number {
		const base = Math.pow(10, ratio);
		return divide(Math.round(times(num, base)), base);
	}

}