import moment from 'moment';

export class BJ_Casino_Rank {

    //#region public

    public ContestData: string[] = ["4/7~4/13", "4/14~4/20", "4/21~4/27"];

    //#endregion

    //#region private

    private _client: any;

    private _ws: any;

    private _rankdata: any[] = [];

    private _nowSearchContestID: 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 = "";

    //#endregion

    //#region Lifecycle

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

    public onLoad() {
        let self: this = this;
        try {
            // const URL = document.getElementById("URL").value;
            // const Port = document.getElementById("Port").value;

            const URL = "wss://game.online-bj.com";
            const Port = "9005";
            const Account = { "p": 0, "device_info": ["Windows", "Windows"], "fcm_token": "", "a": "ct00000691", "pw": "4lsAyoalajm7", "ver": "1.3.0" };

            // const URL = "ws://192.168.5.12";
            // const Port = "9487";

            // const URL = "ws://220.134.195.1";
            // const Port = "19005";
            // const Account = { "p": 0, "device_info": ["Windows", "Windows"], "fcm_token": "", "a": "ct00002242", "pw": "n0tfHlVuEhpO", "ver": "1.3.0" };
            if (this._ws) {
                this.AddLog("中斷上一個連線");
                this._ws.close();
            }
            this._ws = new WebSocket(`${URL}:${Port}`);//连接服务器

            //连接websocket
            this._ws.onopen = function (event: any, AlarmMessage: any) {
                self.AddLog("已經與服務器建立了連接，當前連接狀態：" + this.readyState);
                self.SendData("account.login", Account);
            };

            //websocket传输数据
            this._ws.onmessage = this.OnWebSocketMessage.bind(this);

            //websocket关闭连接
            this._ws.onclose = function (event: any) {
                self.AddLog("已經與服務器斷開連接，當前連接狀態：" + this.readyState);
                // let document = parent ? parent.document : document;
                // document.title = `已斷線`;
            };

            //websocket连接异常
            this._ws.onerror = function (event: any) {
                // alert("WebSocket异常！");
                self.AddLog("WebSocket異常！");
            };
        } catch (ex) {
            // alert(ex.message);
            this.AddLog(ex);
        }
    };

    //#endregion

    //#region Custom

    public SendData(Method: string, Data: any) {
        let json = [Method];
        if (Data != null && Data != undefined) {
            json[1] = Data;
        }

        let str = JSON.stringify(json);
        if (str.length > 65535) {
            this.AddLog("要傳的資料太大囉");
            throw new Error('要傳的資料太大囉');
        }

        if (Data != null && Data != undefined) {
            this.AddLog(`[RPC] 傳送server資料: ${Method}(${JSON.stringify(Data).replace(/\\/g, "")})`);
        } else {
            this.AddLog(`[RPC] 傳送server資料: ${Method}()`);
        }

        let strary = this.GetBytes(str);
        let buffer = new Uint8Array(4 + strary.byteLength);
        let u16ary = new Uint16Array(buffer.buffer, 0, 3);
        u16ary[0] = strary.byteLength;
        buffer[3] = 0x01;
        buffer.set(strary, 4);
        this._ws.send(buffer);
    }

    public ResData(Status: any, Method: any, Data: any = null) {
        // let document = parent ? parent.document : document;
        switch (Method) {
            case "account.login": {
                this.SendData("rank.info", { "t": 12, "p": 2 });
                break;
            }

            case "rank.info": {
                this.RankDataCallBack(Data);
                break;
            }

            case "rank.history": {
                this.ParseRankData(Data);
                this._ws.close();
                break;
            }

            default:
                break;
        }
    }

    public RankDataCallBack(data: any) {
        let id = +data["id"];
        this._nowSearchContestID = id;
        this._nowContestID = id;
        this._nowContestDate = moment().format("MM/DD");
        for (let i = 0; i < this.ContestData.length; i++) {
            const contest: string = this.ContestData[i];
            let str = contest.split("~");
            let my = moment(this._nowContestDate);
            let start = moment(str[0]);
            let end = moment(str[1]);
            let diff: number = my.diff(end, "days");
            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._client.Title.value = `${this._nowContestStart}~${this._nowContestEnd}`;
        this.ParseRankData(data);
    }

    public ParseRankData(data: any) {
        let id = this._nowSearchContestID;
        this.RankDataAddDate(id, data["rank"]);
        this._rankdata = this._rankdata.concat(data["rank"]);
        if (id !== this._nowContestStartIndex) {
            this._nowSearchContestID = id - 1;
            this.SendData("rank.history", { "id": this._nowSearchContestID, "t": 12, "p": 2 });
        } else {
            this.OrganizeRankData(this._rankdata);
            this.SetRankData(this._rankdata);
        }
    }

    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;
    }

    public OrganizeRankData(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._rankdata = rankdata;
    }

    public SetRankData(rankdata: any) {
        this._client.RankData.value = rankdata;
    }

    //#endregion

    //#region Get

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

    //#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;
    }

    //#endregion

    //#region WebSocke

    public OnWebSocketMessage(e: any) {
        let self: this = this;
        if (e.data instanceof ArrayBuffer) {
            this.ParseRpcMessage(e.data);
        } else if (e.data instanceof Blob) {
            let reader = new FileReader();
            reader.onload = (e) => {
                self.ParseRpcMessage(reader.result); reader.onload = null;
            }
            reader.readAsArrayBuffer(e.data);
        } else {
            this.AddLog(`未知的OnWebSocketMessage(e.data)類型: ${e.data}`);
            throw new Error(`未知的OnWebSocketMessage(e.data)類型: ${e.data}`);
        }
    }


    public ParseRpcMessage(buffer: any) {
        let startIndex = 0, byteLength = buffer.byteLength;
        while (startIndex + 4 < byteLength) {
            let strlen = new DataView(buffer, startIndex, 3).getUint16(0, true);
            let str = this.GetString(new Uint8Array(buffer, startIndex + 4, strlen));
            startIndex += strlen + 4;

            try {
                let json = JSON.parse(str);
                let method = json[0];
                let status = json[1][0];
                let data = json[1][1];

                let resp = {
                    Method: method,
                    Status: status,
                    Data: data,
                    IsValid: method && status === 0
                };

                if (data) {
                    this.AddLog(`[RPC] 收到server呼叫:(${resp.Status}): ${resp.Method}(${JSON.stringify(resp.Data)})`);
                    this.ResData(resp.Status, resp.Method, resp.Data);
                } else {
                    this.AddLog(`[RPC] 收到server呼叫:(${resp.Status}): ${resp.Method}()`);
                    this.ResData(resp.Status, resp.Method);
                }
            }
            catch
            {
                this.AddLog(`[RPC] 無法解析Server回應: ${str}`);
                throw new Error(`[RPC] 無法解析Server回應: ${str}`);
            }
        }
    }

    public seestate() {
        // alert(ws.readyState);
        this.AddLog(this._ws.readyState);
    }

    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;
    }

    public GetBytes(str: any) {
        let len = str.length, resPos = -1;
        let resArr = new Uint8Array(len * 3);
        for (let point = 0, nextcode = 0, i = 0; i !== len;) {
            point = str.charCodeAt(i), i += 1;
            if (point >= 0xD800 && point <= 0xDBFF) {
                if (i === len) {
                    resArr[resPos += 1] = 0xef;
                    resArr[resPos += 1] = 0xbf;
                    resArr[resPos += 1] = 0xbd;
                    break;
                }

                nextcode = str.charCodeAt(i);
                if (nextcode >= 0xDC00 && nextcode <= 0xDFFF) {
                    point = (point - 0xD800) * 0x400 + nextcode - 0xDC00 + 0x10000;
                    i += 1;
                    if (point > 0xffff) {
                        resArr[resPos += 1] = (0x1e << 3) | (point >>> 18);
                        resArr[resPos += 1] = (0x2 << 6) | ((point >>> 12) & 0x3f);
                        resArr[resPos += 1] = (0x2 << 6) | ((point >>> 6) & 0x3f);
                        resArr[resPos += 1] = (0x2 << 6) | (point & 0x3f);
                        continue;
                    }
                } else {
                    resArr[resPos += 1] = 0xef;
                    resArr[resPos += 1] = 0xbf;
                    resArr[resPos += 1] = 0xbd;
                    continue;
                }
            }
            if (point <= 0x007f) {
                resArr[resPos += 1] = (0x0 << 7) | point;
            } else if (point <= 0x07ff) {
                resArr[resPos += 1] = (0x6 << 5) | (point >>> 6);
                resArr[resPos += 1] = (0x2 << 6) | (point & 0x3f);
            } else {
                resArr[resPos += 1] = (0xe << 4) | (point >>> 12);
                resArr[resPos += 1] = (0x2 << 6) | ((point >>> 6) & 0x3f);
                resArr[resPos += 1] = (0x2 << 6) | (point & 0x3f);
            }
        }
        return resArr.subarray(0, resPos + 1);
    }

    public GetString(array: any) {
        let str = "";
        let i = 0, len = array.length;
        while (i < len) {
            let c = array[i++];
            switch (c >> 4) {
                case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
                    str += String.fromCharCode(c);
                    break;
                case 12: case 13:
                    str += String.fromCharCode(((c & 0x1F) << 6) | (array[i++] & 0x3F));
                    break;
                case 14:
                    str += String.fromCharCode(((c & 0x0F) << 12) | ((array[i++] & 0x3F) << 6) | ((array[i++] & 0x3F) << 0));
                    break;
            }
        }
        return str;
    }

    //#endregion
}
