import dayjs from "dayjs";
import CSMessage from "./Base/CSMessage";
import { AccountLoginRequest } from "./Base/Request/AccountRequest";
import { AppRankHistory, AppRankInfo } from "./Base/Request/RankRequest";
import "./Engine/CatanEngine/CSharp/String";
import { INetResponse } from "./Engine/CatanEngine/NetManagerV2/Core/INetResponse";
import { NetConnector } from "./Engine/CatanEngine/NetManagerV2/NetConnector";
import { NetManager } from "./Engine/CatanEngine/NetManagerV2/NetManager";
import { Tools } from "./Tools";

export class BJ_Casino_Data {

    //#region public

    public ContestData: string[] = ["5/9~5/15", "5/16~5/22"];

    public Title: string = "";

    public Client: any;

    public Current: any[] = [];

    //#endregion

    //#region private

    private _conn: NetConnector = null!;
    private _ws: any;

    private _rankMagnificationData: any[] = [];
    private _rankWinMoneyData: any[] = [];

    private _nowSearchMagnificationID: number = 0;

    private _nowSearchWinMoneyID: number = 0;

    private _nowContestIndex: number = 0;

    private _nowContestStartIndex: number = 0;

    private _nowContestEndIndex: number = 0;

    private _nowContestID: number = 0;

    private _nowContestDate: string = "";

    private _nowContestStart: string = "";

    private _nowContestEnd: string = "";

    private _isIDupup: boolean = false;

    /**
     * 0 _rankMagnificationData
     * 1 _rankWinMoneyData
     */
    private _isOK: any[] = [false, false];

    //#endregion

    //#region get set

    public get RankMagnificationData(): any[] { return this._rankMagnificationData; }

    public get RankWinMoneyData(): any[] { return this._rankWinMoneyData; }

    //#endregion

    //#region Lifecycle

    /**
     *
     */
    constructor(client: any) {
        this.Client = client;
        this.onLoad();
    }

    public async onLoad(): Promise<void> {
        await this.ConnectServer();
        await this.SendRankData();
    }

    public SetCurrent(current: any): void {
        this.Current.push(current);
    }

    //#endregion

    //#region 網路相關

    /** 連線 */
    public async ConnectServer(): Promise<void> {
        const URL: string = "https://game.online-bj.com";
        const Port: string = "9005";
        await this.ConnectAsync(URL, +Port);
        // 取得帳號資料
        let req: AccountLoginRequest = new AccountLoginRequest("ct00000691", "4lsAyoalajm7");
        await req.SendAsync(true);
        let resp: INetResponse<any> = req.Result;
        if (!resp.IsValid) {
            CSMessage.NetError(resp.Method, resp.Status, "Login Account Error!");
            return;
        }
    }

    /** 連線(目前沒有重連機制) */
    public async ConnectAsync(host: string, port: number): Promise<void> {
        var url: string = "https://api.ipify.org/?format=json";
        var xhr: XMLHttpRequest = new XMLHttpRequest();
        let ip: string = "";
        xhr.onreadystatechange = function (): void {
            if (xhr.readyState === 4 && (xhr.status >= 200 && xhr.status < 400)) {
                ip = JSON.parse(xhr.responseText)["ip"];
            }
        };
        xhr.open("GET", url, true);
        xhr.send();
        console.log("[事件]準備連線...");
        while (ip === "") {
            await Tools.Sleep(1);
        }
        this._conn = new NetConnector(host, port, ip);
        this._conn.OnDataReceived.AddCallback(this._onNetDataReceived, this);
        this._conn.OnDisconnected.AddCallback(this._onNetDisconnected, this);
        NetManager.Initialize(this._conn);
        console.log("[事件]連線中...");
        // 同個connector要再次連線, 可以不用叫CasinoNetManager.Initialize(), 但要先叫CasinoNetManager.Disconnect()
        await NetManager.ConnectAsync();
        console.log(String.Format("[事件]連線狀態: {0}", NetManager.IsConnected));
    }

    private _onNetDisconnected(): void {
        console.log("[事件] 收到連線中斷事件");
        this._conn.OnDataReceived.RemoveAllCallbacks();
        // MainControl.DataReceivedEvent.DispatchCallback([MainControl.DataType.NetDisconnected]);
    }

    private _onNetDataReceived(resp: INetResponse<any>): void {
        console.log(`[事件] 收到server呼叫: ${resp.Method}(${JSON.stringify(resp.Data)}), 狀態: ${resp.Status}`);
        // MainControl.DataReceivedEvent.DispatchCallback([MainControl.DataType.ServerData, resp]);
    }

    //#endregion

    //#region Custom

    public async SendRankData(): Promise<void> {
        this.SendRankMagnificationData();
        this.SendRankWinMoneyData();
    }

    public async SendRankMagnificationData(): Promise<void> {
        let req: any = null;
        req = new AppRankInfo(12, 2);
        await req.SendAsync(true);
        let resp: INetResponse<any> = req.Result;
        if (!resp.IsValid) {
            if (resp.Status === 11) {
                CSMessage.NetError(resp.Method, resp.Status, "Rank 無資料");
                this.OrganizeRankMagnificationData(this._rankMagnificationData);
                this.SetRankMagnificationDataOK();
            } else {
                CSMessage.NetError(resp.Method, resp.Status, "Get RankInfo Fail");
            }
            return;
        }
        this.SetRankMagnificationSearchData(resp.Data);
        this.ParseRankMagnificationData(resp.Data);
    }

    public async SendRankWinMoneyData(): Promise<void> {
        let req: any = null;
        req = new AppRankInfo(11, 2);
        await req.SendAsync(true);
        let resp: INetResponse<any> = req.Result;
        if (!resp.IsValid) {
            if (resp.Status === 11) {
                CSMessage.NetError(resp.Method, resp.Status, "Rank 無資料");
                this.OrganizeRankMagnificationData(this._rankMagnificationData);
                this.SetRankMagnificationDataOK();
            } else {
                CSMessage.NetError(resp.Method, resp.Status, "Get RankInfo Fail");
            }
            return;
        }
        this.SetRankWinMoneySearchData(resp.Data);
        this.ParseRankWinMoneyData(resp.Data);
    }

    public SetRankMagnificationSearchData(data: any): void {
        let id: number = +data["id"];
        this._nowSearchMagnificationID = id;
        this._nowContestID = id;
        this._nowContestDate = dayjs().format("MM/DD");
        for (let i: number = 0; i < this.ContestData.length; i++) {
            const contest: string = this.ContestData[i];
            let str: string[] = contest.split("~");
            let my = this._getdayjsFormString(this._nowContestDate);
            let start: dayjs.Dayjs = this._getdayjsFormString(str[0]);
            let end: dayjs.Dayjs = this._getdayjsFormString(str[1]);
            let diff: number = my.diff(end, "day");
            if (diff <= 0) {
                this._nowContestIndex = i;
                this._nowContestStart = dayjs(start).format("MM/DD");
                this._nowContestEnd = dayjs(end).format("MM/DD");
                this._nowContestStartIndex = this._contestIDFormDate(this._nowContestStart);
                this._nowContestEndIndex = this._contestIDFormDate(this._nowContestEnd);
                break;
            }
        }
        this.Title = `${this._nowContestStart}~${this._nowContestEnd}`;
    }

    public async ParseRankMagnificationData(data: any = null): Promise<void> {
        let id: number = this._nowSearchMagnificationID;
        if (data) {
            this.RankDataAddDate(id, data["rank"]);
            this._rankMagnificationData = this._rankMagnificationData.concat(data["rank"]);
        }
        let targetIndex: number = this._nowContestStartIndex;
        if (this._isIDupup) {
            targetIndex = this._nowContestEndIndex;
        }
        if (id !== targetIndex) {
            if (this._isIDupup) {
                this._nowSearchMagnificationID = id + 1;
            } else {
                this._nowSearchMagnificationID = id - 1;
            }
            let req: any = await this.GetRankData(12, this._nowSearchMagnificationID);
            let resp: INetResponse<any> = req.Result;
            if (!resp.IsValid) {
                if (resp.Status === 11) {
                    CSMessage.NetError(resp.Method, resp.Status, "Rank 無資料");
                    this.ParseRankMagnificationData(resp.Data);
                } else {
                    CSMessage.NetError(resp.Method, resp.Status, "Get RankInfo Fail");
                }
                return;
            }
            this.ParseRankMagnificationData(resp.Data);
            return;
        } else {
            this.OrganizeRankMagnificationData(this._rankMagnificationData);
            this.SetRankMagnificationDataOK();
        }
    }

    public async GetRankData(type: number, dayIdid: number): Promise<any> {
        let req: any = null;
        req = new AppRankHistory(type, 2, dayIdid);
        if (dayIdid === this._nowContestID) {
            req = new AppRankInfo(type, 2);
        }
        await req.SendAsync(true);
        return req;
    }

    public OrganizeRankMagnificationData(rankdata: any): void {
        rankdata.sort((a: any, b: any) => {
            return b[1] - a[1];
        });
        rankdata = rankdata.filter((rankdata: any, index: any, arr: any) => {
            return arr.findIndex((s: any) => rankdata[2][1] === s[2][1]) === index;
        });
        for (let i: number = 0; i < rankdata.length; i++) {
            rankdata[i][0] = i + 1;
        }
        this._rankMagnificationData = rankdata;
    }

    public SetRankMagnificationDataOK(): void {
        this._isOK[0] = true;
        this._checkOK();
    }

    public SetRankWinMoneySearchData(data: any): void {
        let id: number = +data["id"];
        this._nowSearchWinMoneyID = id;
    }

    public async ParseRankWinMoneyData(data: any = null): Promise<void> {
        let id: number = this._nowSearchWinMoneyID;
        if (data) {
            this.RankDataAddDate(id, data["rank"]);
            this._rankWinMoneyData = this._rankWinMoneyData.concat(data["rank"]);
        }
        let targetIndex: number = this._nowContestStartIndex;
        if (this._isIDupup) {
            targetIndex = this._nowContestEndIndex;
        }
        if (id !== targetIndex) {
            if (this._isIDupup) {
                this._nowSearchWinMoneyID = id + 1;
            } else {
                this._nowSearchWinMoneyID = id - 1;
            }
            let req: any = await this.GetRankData(11, this._nowSearchWinMoneyID);
            let resp: INetResponse<any> = req.Result;
            if (!resp.IsValid) {
                if (resp.Status === 11) {
                    CSMessage.NetError(resp.Method, resp.Status, "Rank 無資料");
                    this.ParseRankWinMoneyData();
                } else {
                    CSMessage.NetError(resp.Method, resp.Status, "Get RankInfo Fail");
                }
                return;
            }
            this.ParseRankWinMoneyData(resp.Data);
            return;
        } else {
            this.OrganizeRankWinMoneyData(this._rankWinMoneyData);
            this.SetRankWinMoneyDataOK();
        }
    }

    public OrganizeRankWinMoneyData(rankdata: any): void {
        rankdata.sort((a: any, b: any) => {
            return b[1] - a[1];
        });
        rankdata = rankdata.filter((rankdata: any, index: any, arr: any) => {
            return arr.findIndex((s: any) => rankdata[2][1] === s[2][1]) === index;
        });
        for (let i: number = 0; i < rankdata.length; i++) {
            rankdata[i][0] = i + 1;
        }
        this._rankWinMoneyData = rankdata;
    }

    public SetRankWinMoneyDataOK(): void {
        this._isOK[1] = true;
        this._checkOK();
    }

    public RankDataAddDate(id: number, rankdata: any): any {
        let date: string = this._contestDateFormID(id);
        for (let i: number = 0; i < rankdata.length; i++) {
            rankdata[i].push(date);
        }
        return rankdata;
    }

    private _checkOK(): void {
        if (this._isOK.includes(false)) {
            return;
        }
        NetManager.Disconnect();
        for (let i: number = 0; i < this.Current.length; i++) {
            const current: any = this.Current[i];
            current.SetInit();
        }
        this.Client.isLoading.value = false;
    }

    public async PrevWeek(): Promise<void> {
        let nowContestIndex: number = this._nowContestIndex;
        if (nowContestIndex - 1 >= 0) {
            nowContestIndex--;
        } else {
            alert(`到底了`);
            return;
        }
        this._isIDupup = false;
        await this.RunSetWeek(nowContestIndex);
        // const contest: string = this.ContestData[nowContestIndex];
        // let str: string[] = contest.split("~");
        // let my: dayjs.Dayjs = this._getdayjsFormString(this._nowContestDate);
        // let start: dayjs.Dayjs = this._getdayjsFormString(str[0]);
        // let end: dayjs.Dayjs = this._getdayjsFormString(str[1]);
        // let diff: number = my.diff(start, "day");
        // if (diff < 0) {
        //     alert(`還沒開放`);
        //     return;
        // }
        // this.Client.isLoading.value = true;
        // this._isOK[0] = false;
        // this._isOK[1] = false;

        // this._nowContestStart = dayjs(start).format("MM/DD");
        // this._nowContestEnd = dayjs(end).format("MM/DD");
        // this._nowContestStartIndex = this._contestIDFormDate(this._nowContestStart);
        // this._nowContestEndIndex = this._nowSearchMagnificationID = this._contestIDFormDate(this._nowContestEnd);
        // this.Title = `${this._nowContestStart}~${this._nowContestEnd}`;

        // await this.ConnectServer();
        // this.RunRankMagnificationWeek();
        // this.RunRankWinMoneyWeek();
    }

    public async NextWeek(): Promise<void> {
        let nowContestIndex: number = this._nowContestIndex;
        if (nowContestIndex + 1 < this.ContestData.length) {
            nowContestIndex++;
        } else {
            alert(`到底了`);
            return;
        }

        this._isIDupup = true;
        await this.RunSetWeek(nowContestIndex);
    }

    public async RunSetWeek(nowContestIndex: number): Promise<void> {
        const contest: string = this.ContestData[nowContestIndex];
        let str: string[] = contest.split("~");
        let my: dayjs.Dayjs = this._getdayjsFormString(this._nowContestDate);
        let start: dayjs.Dayjs = this._getdayjsFormString(str[0]);
        let end: dayjs.Dayjs = this._getdayjsFormString(str[1]);
        let diff: number = my.diff(start, "day");
        if (diff < 0) {
            alert(`還沒開放`);
            return;
        }
        this._nowContestIndex = nowContestIndex;
        this.Client.isLoading.value = true;
        this._isOK[0] = false;
        this._isOK[1] = false;

        this._nowContestStart = dayjs(start).format("MM/DD");
        this._nowContestEnd = dayjs(end).format("MM/DD");
        this._nowContestStartIndex = this._contestIDFormDate(this._nowContestStart);
        this._nowContestEndIndex = this._contestIDFormDate(this._nowContestEnd);
        if (this._isIDupup) {
            this._nowSearchMagnificationID = this._nowSearchWinMoneyID = this._contestIDFormDate(this._nowContestStart);
        } else {
            this._nowSearchMagnificationID = this._nowSearchWinMoneyID = this._contestIDFormDate(this._nowContestEnd);
        }
        this.Title = `${this._nowContestStart}~${this._nowContestEnd}`;

        await this.ConnectServer();
        this.RunRankMagnificationWeek();
        this.RunRankWinMoneyWeek();
    }

    public async RunRankMagnificationWeek(): Promise<void> {
        this._rankMagnificationData = [];
        let req: any = await this.GetRankData(12, this._nowSearchMagnificationID);
        let resp: INetResponse<any> = req.Result;
        if (!resp.IsValid) {
            if (resp.Status === 11) {
                CSMessage.NetError(resp.Method, resp.Status, "Rank 無資料");
                this.OrganizeRankMagnificationData(this._rankMagnificationData);
                this.SetRankMagnificationDataOK();
            } else {
                CSMessage.NetError(resp.Method, resp.Status, "Get RankInfo Fail");
            }
            return;
        }
        this.ParseRankMagnificationData(resp.Data);
    }

    public async RunRankWinMoneyWeek(): Promise<void> {
        this._rankWinMoneyData = [];
        let req: any = await this.GetRankData(11, this._nowSearchWinMoneyID);
        let resp: INetResponse<any> = req.Result;
        if (!resp.IsValid) {
            if (resp.Status === 11) {
                CSMessage.NetError(resp.Method, resp.Status, "Rank 無資料");
                this.OrganizeRankWinMoneyData(this._rankWinMoneyData);
                this.SetRankWinMoneyDataOK();
            } else {
                CSMessage.NetError(resp.Method, resp.Status, "Get RankInfo Fail");
            }
            return;
        }
        this.ParseRankWinMoneyData(resp.Data);
    }

    //#endregion

    //#region Get

    private _contestIDFormDate(date: string): number {
        let my: dayjs.Dayjs = this._getdayjsFormString(this._nowContestDate);
        let target: dayjs.Dayjs = this._getdayjsFormString(date);
        let diffday: number = my.diff(target, "day");
        let id: number = this._nowContestID - diffday;
        if (id < 0) {
            return 0;
        } else {
            return id;
        }
    }

    private _contestDateFormID(id: number): string {
        let my: number = this._nowContestID;
        let target: number = id;
        let diffid: number = my - target;
        if (diffid < 0) {
            diffid = 0;
        }
        let date: string = dayjs().subtract(diffid, "day").format("MM/DD");
        return date;
    }

    private _getdayjsFormString(str: string): dayjs.Dayjs {
        let data: string[] = str.split("/");
        let m: number = +data[0] - 1;
        let d: number = +data[1];
        let date: Date = new Date();
        date.setMonth(m);
        date.setDate(d);
        let mydayjs: dayjs.Dayjs = dayjs(date);
        return mydayjs;
    }

    //#endregion

    //#region Tools Function

    /**
     * 陣列排序，asc&key陣列長度請一樣
     * PS. boolean 帶false是先true在false
     * @link JavaScript Object 排序 http://www.eion.com.tw/Blogger/?Pid=1170#:~:text=JavaScript%20Object%20排序
     * @param Arr 需排序陣列
     * @param asc 是否升序排列(小到大)
     * @param key 需排序的key(優先順序左到右)(沒有就放空)
     */
    public ObjectSort(Arr: any[], asc: boolean[] = [true], key?: string[]): any[] {
        if (!key || key.length === 0 || Arr.length === 0 || Arr[0][key[0]] === undefined) {
            console.error(`ObjectSort key error`);
            return Arr;
        } else if (asc.length !== key.length) {
            console.error(`ObjectSort key asc error asc.length: ${asc.length}, key.length: ${key.length}`);
            return Arr;
        }
        let count: number = key ? key.length : 1;
        for (let i: number = count - 1; i >= 0; i--) {
            Arr = Arr.sort(function (a: any, b: any): 1 | -1 {
                let mya: any = a;
                let myb: any = b;
                if (key) {
                    mya = a[key[i]];
                    myb = b[key[i]];
                }

                // 加個等於數字相同不要再去排序到
                if (asc[i]) {
                    return mya >= myb ? 1 : -1;
                } else {
                    return mya <= myb ? 1 : -1;
                }
            });
        }
        return Arr;
    }

    public Sleep(ms: number): Promise<any> {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    public AddLog(log: any): void {
        console.log(log);
        // var textarea = document.getElementById("Log");
        // textarea.value += log + "\n";
        // textarea.scrollTop = textarea.scrollHeight;
    }

    //#endregion
}
