import { string } from 'mathjs';
import { ApiCall } from 'tsrpc';
import { prizeType } from '../protocols/type';
import { player } from '../protocols/user/type';

export class PublicShared {
    /**合并奖励 */
    static mergePrize(prize: prizeType[]): prizeType[] {
        let typeObj: k_v<k_v<{
            n: number;
            colour?: number;
        }>> = {};
        let mergeArr: prizeType[] = [];

        prize.forEach(atn => {
            if (!typeObj[atn.a]) typeObj[atn.a] = {};
            if (!typeObj[atn.a][atn.t]) typeObj[atn.a][atn.t] = { n: 0, colour: atn.colour || 0 };
            typeObj[atn.a][atn.t].n += atn.n;
        });
        for (let type in typeObj) {
            for (let id in typeObj[type]) {
                mergeArr.push({
                    a: type,
                    t: id,
                    n: typeObj[type][id].n,
                    colour: typeObj[type][id].colour
                });
            }
        }
        return mergeArr;
    }

    static exprStr2Compile = {};
    /**eval预编译 */
    static eval(expr:any, scope?:object){
        if(Array.isArray(expr)) {
            let res = []
            expr.forEach( item => {
                if(! this.exprStr2Compile[item] ){
                    this.exprStr2Compile[item] = mathJs.compile(item);
                }
                res.push(this.exprStr2Compile[item].evaluate(scope))
            })
            return res
        } else {
            if(! this.exprStr2Compile[expr] ){
                this.exprStr2Compile[expr] = mathJs.compile(expr);
            }
            return this.exprStr2Compile[expr].evaluate(scope);
        }
    }

    /**合并属性 */
    static mergeProperty(obj1: any = {}, obj2: any = {}) {
        for (let k in obj2) {

            if (obj2[k] instanceof Array) {
                if (obj1[k]) obj1[k] = obj1[k].concat(obj2[k]);
                else obj1[k] = obj2[k];
            } else if (obj2[k] instanceof Object) {
                if (obj1[k]) PublicShared.mergeProperty(obj1[k], obj2[k]);
                else {
                    obj1[k] = {};
                    PublicShared.mergeProperty(obj1[k], obj2[k]);
                }
            } else {
                if (obj1[k]) obj1[k] += obj2[k];
                else obj1[k] = obj2[k];
            }
        }
    }
    /**
     * 随机掉落组
     * @param id 掉落组id
     * @param num 随机次数
     * @param conf
     */
    static randomDropGroup(id: string | number, num = 1, conf = G.gc.diaoluo): prizeType[] {
        let prize = [];
        let weight = [];
        let dlz = JSON.parse(JSON.stringify(conf[id]));

        dlz.forEach((d, index) => {
            if (weight[index - 1]) {
                weight.push(weight[index - 1] + d.p);
            } else {
                weight.push(d.p);
            }
        });

        for (let n = 0; n < num; n++) {
            let prizeIndex = 0;
            // debug 举例：diaoluo.json 250001参数的[p:1, p:140], 随机数范围是 1-141，小于等于1的概率较小，所以大部分取后面的值，后面的值给的物品数量是0
            let randomNum = this.randomNum(1, weight[weight.length - 1]);

            for (let index = 0; index < weight.length; index++) {
                if (randomNum <= weight[index]) {
                    prizeIndex = index;
                    break;
                }
            }

            prize.push(dlz[prizeIndex]);
        }

        return prize;
    }
    /**
     * 随机概率组
     */
    static randomDropAny<T extends { p: number; }>(dropArr: T[]): T {
        let dropGrop = { 0: dropArr };
        return this.randomDropGroup(0, 1, dropGrop as any)[0] as any;
    }
    /**
     * 随机生成区间范围整数
     */
    static randomNum(min: number, max: number) {
        return Math.floor(Math.random() * (max - min + 1) + min);
    }
    /**获取道具配置 */
    static getAtnConf(atn: prizeType) {
        if (atn.a == 'attr') return G.gc.attr[atn.t];
        else if (atn.a == 'item') return G.gc.item[atn.t];
        else if (atn.a == 'hero') return G.gc.hero[atn.t];
        else if (atn.a == 'equip') return G.gc.equip[atn.t];

        return null;
    }
    /**获取当天0点时间戳 */
    static getToDayZeroTime(time: string): number;
    static getToDayZeroTime(time?: number): number;
    static getToDayZeroTime(time: string | number) {
        let t = typeof time == 'string' ? new Date(time) : new Date((time || G.time) * 1000);
        return Math.round(t.setHours(0, 0, 0, 0) / 1000);
    }
    /**获取本周周一0点时间戳 */
    static getToWeekMondayZeroTime(time = G.time) {
        let toDay = new Date(time * 1000);
        let day = toDay.getDay() || 7;
        let z = new Date(toDay.getFullYear(), toDay.getMonth(), toDay.getDate() + 1 - day);
        return Math.round(z.getTime() / 1000);
    }

    /**获取当前第几周  避免跨年导致key不一样，以周一时间戳去计算*/
    static getToWeek(time = this.getToWeekMondayZeroTime()) {
        let toDay = new Date(time * 1000);
        let day = toDay.getDay() || 7;
        let z = new Date(toDay.getFullYear(), toDay.getMonth(), toDay.getDate() + 1 - day);

        let z2 = new Date(toDay.getFullYear(), 0, 1);

        let rq = (z.getTime() - z2.getTime()) / 1000;
        let s1 = Math.ceil(rq / (24 * 60 * 60));
        let s2 = Math.ceil(s1 / 7);
        return `${toDay.getFullYear()}_${s2}`;
    }

    /**周几 */
    static getWeek(timestamp: number) {
        let toDay = new Date(timestamp * 1000);
        const day = toDay.getDay();
        return day
    }

    /**是否同一天 */
    static chkSameDate(time: string | number, chktime: string | number) {
        if (this.getToDayZeroTime(time as string) === this.getToDayZeroTime(chktime as string))
            return true;
        return false;
    }
    /**获取今天是开服第几天 */
    static getOpenServerDay(openTime: any = G.openTime, time = G.time) {
        let oDay = this.getToDayZeroTime(openTime);
        return Math.ceil((time - oDay) / 86400);
    }
    /**获取相差天数 */
    static getDiff(oldTime: number, time = G.time) {
        let oDay = this.getToDayZeroTime(oldTime);
        return Math.ceil((time - oDay) / 86400);
    }

    /**数组buff转对象buff */
    static arrBuffToObjectBuff(arrBuff: (string | number)[]) {
        let buff = {};
        for (let i = 0; i < arrBuff.length; i++) {
            if (i % 2 != 0) continue;
            buff[arrBuff[i]] = arrBuff[i + 1];
        }
        return buff;
    }
    static str(str: string, ...args: Array<number | string>) {
        if (arguments.length < 2) return str;
        if (arguments.length == 2 && Object.prototype.toString.call(arguments[1]) == '[object Array]') {
            let args = arguments[1];
            for (let i = 0; i < args.length; i++) {
                str = str.replace(new RegExp('\\{' + (i + 1) + '\\}', 'g'), args[i]);
            }
        } else {
            for (let i = 1; i < arguments.length; i++) {
                str = str.replace(new RegExp('\\{' + i + '\\}', 'g'), arguments[i]);
            }
        }
        return str;
    }

    static getEventIsOpen(cond: k_v<number>, player: Partial<player>) {

        let arr = Object.entries(cond);

        for (let [k, v] of arr) {
            if (k == 'day' && this.getOpenServerDay() < v) return false;
            if (player[k] < v) return false;
        }
        return true;
    }

    /**转化时间戳显示日期 */
    static fmtTime(timestamp: number) {
        const date = new Date(timestamp * 1000);
        const year = date.getFullYear();
        const month = date.getMonth() + 1; // 获取月份，需要加1
        const day = date.getDate(); // 获取日期
        const hour = date.getHours(); // 获取小时
        const minute = date.getMinutes(); // 获取分钟
        const second = date.getSeconds(); // 获取秒数
        // const formattedDate = `${year}-${month}-${day} ${hour}:${minute}:${second}`; // 拼接成格式化后的日期字符串
        const formattedDate = `${month}月${day}日${hour}:${minute}`; // 拼接成格式化后的日期字符串
        return formattedDate;
    }

    /**功能开启 */
    static getOpenCond(call: ApiCall, gongneng: string): [boolean, string] {
        let _con = G.gc.openCond[gongneng]
        if (!_con) return [false, "功能不存在"]

        let _res = false
        let _and = Object.keys(_con.and)
        for (let index = 0; index < _and.length; index++) {
            _res = true
            const element = _and[index];
            if (element == "lv" && call.conn.gud.lv < _con.and[element]) {
                _res = false
            } else if (element == "openTime" && this.getOpenServerDay() < _con.and[element]) {
                _res = false
            } else if (element == "vip" && call.conn.gud.vip < _con.and[element]) {
                _res = false
            } else if (element == "mapId" && call.conn.gud.mapId < _con.and[element]) {
                _res = false
            }

            if (!_res) return [false, _con.tips]
        }

        let _or = Object.keys(_con.or)
        for (let index = 0; index < _or.length; index++) {
            _res = false
            const element = _or[index];
            if (element == "lv" && call.conn.gud.lv >= _con.or[element]) {
                _res = true
                break
            } else if (element == "openTime" && this.getOpenServerDay() >= _con.or[element]) {
                _res = true
                break
            } else if (element == "vip" && call.conn.gud.vip >= _con.or[element]) {
                _res = true
                break
            } else if (element == "mapId" && call.conn.gud.mapId >= _con.or[element]) {
                _res = true
                break
            }
        }

        return [_res, _con.tips];
    }

}