import moment from "moment";
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[] = ["4/7~4/13", "4/14~4/20", "4/21~4/27"];

    public Title: string = "";

    public Client: 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 = "";

    /**
     * 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> {
        // CoroutineV2.Single(this.aaa()).Start();
        let self: this = this;
        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;
        }
        await this.SendRankData();
    }
    public *aaa(): IterableIterator<any> {
        console.log("aaa");
    }

    //#endregion

    //#region 網路相關

    /** 連線(目前沒有重連機制) */
    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 無資料");
            } else {
                CSMessage.NetError(resp.Method, resp.Status, "Get RankInfo Fail");
            }
            return;
        }
        await this.RankMagnificationDataCallBack(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 無資料");
            } else {
                CSMessage.NetError(resp.Method, resp.Status, "Get RankInfo Fail");
            }
            return;
        }
        await this.RankWinMoneyDataCallBack(resp.Data);
    }

    public async RankMagnificationDataCallBack(data: any) {
        let id: number = +data["id"];
        this._nowSearchMagnificationID = id;
        this._nowContestID = id;
        this._nowContestDate = moment().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: moment.Moment = this._getMomentFormString(this._nowContestDate);
            let start: moment.Moment = this._getMomentFormString(str[0]);
            let end: moment.Moment = this._getMomentFormString(str[1]);
            let diff: number = my.diff(end, "day");
            if (diff <= 0) {
                this._nowContestIndex = i;
                this._nowContestStart = moment(start).format("MM/DD");
                this._nowContestEnd = moment(end).format("MM/DD");
                this._nowContestStartIndex = this._contestIDFormDate(this._nowContestStart);
                this._nowContestEndIndex = this._contestIDFormDate(this._nowContestEnd);
                break;
            }
        }
        this.Title = `${this._nowContestStart}~${this._nowContestEnd}`;
        this.ParseRankMagnificationData(data);
    }

    public async ParseRankMagnificationData(data: any) {
        let id = this._nowSearchMagnificationID;
        this.RankDataAddDate(id, data["rank"]);
        this._rankMagnificationData = this._rankMagnificationData.concat(data["rank"]);
        if (id !== this._nowContestStartIndex) {
            this._nowSearchMagnificationID = id - 1;
            // this.SendData("rank.history", { "id": this._nowSearchContestID, "t": 12, "p": 2 });
            let req: any = null;
            req = new AppRankHistory(12, 2, this._nowSearchMagnificationID);
            await req.SendAsync(true);
            let resp: INetResponse<any> = req.Result;
            if (!resp.IsValid) {
                if (resp.Status == 11) {
                    CSMessage.NetError(resp.Method, resp.Status, "Rank 無資料");
                } else {
                    CSMessage.NetError(resp.Method, resp.Status, "Get RankInfo Fail");
                }
                return;
            }
            this.ParseRankMagnificationData(resp.Data);
            return;
        } else {
            this.OrganizeRankMagnificationData(this._rankMagnificationData);
            this.SetRankMagnificationData(this._rankMagnificationData);
        }
    }

    public OrganizeRankMagnificationData(rankdata: any) {
        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 = 0; i < rankdata.length; i++) {
            rankdata[i][0] = i + 1;
        }
        this._rankMagnificationData = rankdata;
    }

    public SetRankMagnificationData(rankdata: any) {
        this._isOK[0] = true;
        this._checkOK();
    }

    public async RankWinMoneyDataCallBack(data: any) {
        let id = +data["id"];
        this._nowSearchWinMoneyID = id;
        this.ParseRankWinMoneyData(data);
    }

    public async ParseRankWinMoneyData(data: any) {
        let id = this._nowSearchWinMoneyID;
        this.RankDataAddDate(id, data["rank"]);
        this._rankWinMoneyData = this._rankWinMoneyData.concat(data["rank"]);
        if (id !== this._nowContestStartIndex) {
            this._nowSearchWinMoneyID = id - 1;
            let req: any = null;
            req = new AppRankHistory(11, 2, this._nowSearchWinMoneyID);
            await req.SendAsync(true);
            let resp: INetResponse<any> = req.Result;
            if (!resp.IsValid) {
                if (resp.Status == 11) {
                    CSMessage.NetError(resp.Method, resp.Status, "Rank 無資料");
                } else {
                    CSMessage.NetError(resp.Method, resp.Status, "Get RankInfo Fail");
                }
                return;
            }
            this.ParseRankWinMoneyData(resp.Data);
            return;
        } else {
            this.OrganizeRankWinMoneyData(this._rankWinMoneyData);
            this.SetRankWinMoneyData(this._rankWinMoneyData);
        }
    }

    public OrganizeRankWinMoneyData(rankdata: any) {
        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 = 0; i < rankdata.length; i++) {
            rankdata[i][0] = i + 1;
        }
        this._rankWinMoneyData = rankdata;
    }

    public SetRankWinMoneyData(rankdata: any) {
        this._isOK[1] = true;
        this._checkOK();
    }

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

    private _checkOK() {
        if (this._isOK.includes(false)) {
            return;
        }
        NetManager.Disconnect();
        this.Client.isLoading.value = false;
    }

    //#endregion

    //#region Get

    private _contestIDFormDate(date: string): number {
        let my = this._getMomentFormString(this._nowContestDate);
        let target = this._getMomentFormString(date);
        let diffday: number = my.diff(target, "day");
        let id = 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 = moment().subtract(diffid, "day").format("MM/DD");
        return date;
    }

    private _getMomentFormString(str: string): moment.Moment {
        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 mymoment: moment.Moment = moment(date);
        return mymoment;
    }

    //#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) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

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

    //#endregion
}
