import CSMessage from "@/Common/Message/CSMessage";
import { INetResponse } from "@/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
import CSSettingsSDV3 from "@/FormTableSD/CSSettingsSDV3";
import { gameObj } from "@/context/GameItemsContext";
import GameManager from "@/modules/GameManager";
import { NumberEx } from "@/utils/Number/NumberEx";
import { CommonSlotAERequest, CommonSlotFgSpinRequest, CommonSlotSpinRequest } from "../Request/CommonSlotRequest";
import { Slot_ChoiceRequest } from "../Request/SlotRequest";


export class SlotBase {
	//#region public

	public get ID(): number { return this.id; }
	public get FreeID(): number { return 1; }
	public get FreeCount(): number { return +this.GameRunData["free"][1]; }
	public get HasChoiceFreeGame(): boolean { return false; }
	public get SlotReqRespIsCount(): boolean { return false; }
	public get HasRetriggerFreeSpin(): boolean { return false; }
	/** Spin協定的mode欄位(null=不帶, Cluster系列機台需帶0) */
	public get SpinMode(): number { return null; }
	public GameRunData: JSON = null;

	//#endregion

	//#region protected

	protected id: number;

	//#endregion

	//#region private

	private addLog: (v: string) => void;

	//#endregion

	//#region Lifecycle

	/**
	 *
	 */
	constructor(id: number, addLog: (v: string) => void) {
		this.id = id;
		this.addLog = addLog;
		this.onLoad();
	}

	public async onLoad(): Promise<void> {
		//
	}

	//#endregion

	//#region Custom

	public *Spin(bet: number, OnClickStop: () => void): IterableIterator<any> {
		const { player, setPlayer } = gameObj;
		let gameRunData: JSON = null;
		let req: CommonSlotSpinRequest = new CommonSlotSpinRequest(this.id, bet, this.SpinMode);
		yield req.SendAsync();
		let resp: INetResponse<JSON> = req.Result;
		if (resp.IsValid) {
			gameRunData = this.GameRunData = resp.Data;
		} else {
			CSMessage.NetError(resp.Method, resp.Status);
			this.addLog(`Slot${this.ID} Spin失敗 [${resp.Method}] status:${resp.Status}`);
			OnClickStop();
			return;
		}
		let money: number = gameRunData["money"] ? +gameRunData["money"] : 0;
		let winMoney: number = 0;
		let winMoneyLog: string = "";
		let freeLog: string = "";
		let resources: any[] = gameRunData["get"];
		let free: any = gameRunData["free"];
		let choiceFreeGame: boolean = gameRunData["rs"] === 0;
		if (resources) {
			winMoney = this._getWinMoney(resources);
		}
		if (choiceFreeGame && this.HasChoiceFreeGame) {
			free = true;
		}
		if (free) {
			let freeCount: number = yield* this._getFreeCount();
			let fsWinMoney: number = 0;
			let fsMoney: number = 0;
			[freeCount, fsWinMoney, fsMoney] = yield* this.FreeSpin(freeCount);
			if (fsMoney > 0) {
				money = fsMoney;
			}
			if (fsWinMoney > 0) {
				winMoney = fsWinMoney;
			}
			freeLog = `, hasFree: ${freeCount}`;
		}
		if (winMoney > 0) {
			winMoneyLog = `, winMoney: ${winMoney}`;
		}
		setPlayer({ ...player, m: money });
		let ratio: number = winMoney > 0 ? NumberEx.divide(winMoney, GameManager.SlotData.NowBet) : 0;
		if (GameManager.SlotData.IsRatioStop && ratio >= GameManager.SlotData.RatioStop) {
			OnClickStop();
		}
		if (GameManager.SlotData.IsCountStop && GameManager.SlotData.CountStop === 0) {
			OnClickStop();
		}
		if (ratio > 100) {
			this.addLog(`Slot${this.ID} Spin Bet: ${GameManager.SlotData.NowBet}, Money: ${money}${winMoneyLog}${freeLog}, Ratio: ${ratio}`);
		}
		this.addLog(`Slot${this.ID} Spin Bet: ${GameManager.SlotData.NowBet}, Money: ${money}${winMoneyLog}${freeLog}`);
		if (ratio > 0) {
			this.slotAE();
		}
	}

	public * FreeSpin(freeCount: number) {
		let fsWinMoney: number = 0;
		let fsMoney: number = 0;
		for (let i: number = 0; i < freeCount; i++) {
			let gameRunData: JSON = null;
			let req: CommonSlotFgSpinRequest = new CommonSlotFgSpinRequest(this.ID);
			yield req.SendAsync();
			let resp: INetResponse<JSON> = req.Result;
			if (resp.IsValid) {
				gameRunData = resp.Data;
				if (this.HasRetriggerFreeSpin) {
					let reTriggerCount: number = this._getRetriggerFreeSpinCount(gameRunData);
					freeCount += reTriggerCount;
				}
				if (i === freeCount - 1) {
					let resources: any[] = gameRunData["get"];
					if (resources) {
						fsWinMoney = this._getWinMoney(resources);
					}
					fsMoney = gameRunData["money"] ? +gameRunData["money"] : 0;
				}
			} else {
				CSMessage.NetError(resp.Method, resp.Status);
			}
			this.addLog(`Slot${this.ID} FreeSpin MaxCount: ${freeCount}, Count: ${i + 1}`);
		}
		return [freeCount, fsWinMoney, fsMoney];
	}

	protected _getWinMoney(resources: any[]): number {
		for (let i: number = 0; i < resources.length; i++) {
			const resource: any[] = resources[i];
			if (resource[0] === 1) {
				return resource[1];
			}
		}
		return 0;
	}

	protected _getRetriggerFreeSpinCount(gameRunData: JSON): number {
		if (gameRunData["free"]) {
			return gameRunData["free"][1];
		}
		return 0;
	}

	protected *_getFreeCount() {
		if (this.HasChoiceFreeGame) {
			return yield* this._getChoiceFreeCount();
		} else {
			return this.FreeCount;
		}
	}

	protected * _getChoiceFreeCount() {
		let id: number = this._getFreeID();
		let request: Slot_ChoiceRequest = new Slot_ChoiceRequest(id);
		yield request.SendAsync(true);
		let result: INetResponse<number> = request.Result;
		if (result.IsValid) {
			if (this.SlotReqRespIsCount) {
				return result.Data;
			} else {
				let slotNameSetting: string = CSSettingsSDV3.Slotset[this.ID].NameSetting;
				let free_info_id: number = result.Data;
				let slotSetting: any = CSSettingsSDV3[slotNameSetting];
				let freeInfo: any = slotSetting.FreeInfo;
				let count: number = freeInfo[free_info_id].Spins;
				return count;
			}
		}
		return 0;
	}

	protected _getFreeID(): number {
		return this.FreeID;
	}

	private slotAE(): void {
		let req: CommonSlotAERequest = new CommonSlotAERequest();
		req.Send();
	}

	//#endregion
}

export default SlotBase;
