import { screen, Asset, assetManager, error, Node, Vec3, sp, tween, Layers, find, Camera, v2, UITransform, v3, isValid, CCObject, AssetManager, director, Prefab } from 'cc';
import { GameConst, ITEM_TYPE } from '../common/GameConst';
import { PoolManager } from '../manager/PoolManager';
import { bundleMgr } from '../manager/BundleManager';

export default class Utils {
    public static loadedAssets: Map<string, Asset> = new Map();
    private static bundles: Map<string, AssetManager.Bundle> = new Map();

    /**
     * 验证字符串是否是空的
     * @s 字符串
     */
    public static isNull(s: string) {
        if(s == "" 
            || s == null 
            || s == undefined){
            return true;
        }
        if (typeof s === 'string'){
            let re = new RegExp("^[ ]+$");
            return re.test(s);
        }else{
            return false;
        }
    }
    
    /**
     * 数组移除某一个元素
     */
    public static remove(arr,param){
        let index = arr.indexOf(param)
        if (index > -1) {
            arr.splice(index, 1)
        }
    }

    /**
     * 查询一个节点下的子节点
     */
    public static findName(root: Node, name: string) : Node{
        let child: Node;
        if(name.indexOf("/") != -1) {
            child = find(name, root);
        }else {
            if(!root) {root = director.getScene();}
            if (root.name === name) {
                child = root;
            } else {
                child = this.findChild(name, root);
            }
        }
        if(child) {
            return child;
        } else {
            console.log("没有找到指定的Node, node name ==", name);
            return null;
        }
    }

    private static findChild(name: string, parent: Node): Node {
        let child: Node = parent.getChildByName(name);
        if (child) {
            return child;
        } else {
            let children: Node[] = parent.children;
            for (let i = 0; i < children.length; i++) {
                child = this.findChild(name, children[i]);
                if (child) {
                    return child;
                }
            }
        }
        return null;
    }

    
    //检查是否同一天
    public static checkIsToDay(time: number): boolean {

        var date = new Date(time);
        var dateNow = new Date()
        let bSameDay = false;

        if (date.getFullYear() == dateNow.getFullYear() &&
            date.getMonth() == dateNow.getMonth() &&
            date.getDate() == dateNow.getDate()
        ) {
            bSameDay = true;
        }
        return bSameDay;
    }



    public static getRandom(lower, upper): number {
        return Math.random() * (upper - lower) + lower;
    };

    public static getRandomInt(lower, upper): number {
        return Math.floor(Math.random() * (upper - lower)) + lower;
    };

    public static seed: number = 5;

    public static seedRandom(): number {
        return Utils.getRandom(0, 1);
        // this.seed = (this.seed * 9301 + 49297) % 233280;
        // return this.seed / 233280.0;
    }

    public static seedRandomInt(lower, upper): number {
        return Utils.getRandomInt(lower, upper);
        // return Math.floor(Utils.seedRandom() * (upper - lower)) + lower;
    }

    // public static formatNumber(num: number, afterdot: number = 1) {
    //     num = Math.floor(num);
    //     return BigNumber.getLargeString(num);
    // };
    public static getPowNum(p) {
        return Math.pow(10, p);
    };


    // static showTip(value: string) {
    //     if (sys.platform == sys.WECHAT_GAME) {
    //         wx.showToast({ title: value, icon: 'none', duration: 1500 })
    //     }
    // }

    static formatDate(t) {
        var date = new Date(t);
        var YY = date.getFullYear() + '-';
        var MM = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
        var DD = (date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate());
        var hh = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':';
        var mm = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()) + ':';
        var ss = (date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds());
        return YY + MM + DD + " " + hh + mm + ss;
    }

    public static cloneObj(obj: any) {
        obj = JSON.stringify(obj);
        obj = JSON.parse(obj);
        return obj;
    }

    public static getTimeStrByS(second: number) {
        second = Math.floor(second);
        if (second < 0) second = 0;
        var d = Math.floor(second / 3600 / 24);
        second -= d * 3600 * 24;
        var h = Math.floor(second / 3600);
        second -= h * 3600;
        var m = Math.floor(second / 60);
        second -= m * 60;
        var front = "00";
        if (h > 9) {
            front = "" + h;
        } else {
            front = "0" + h;
        }
        var mid = "00";
        if (m > 9) {
            mid = "" + m;
        } else {
            mid = "0" + m;
        }
        var back = "00";
        if (second > 9) {
            back = "" + second;
        } else {
            back = "0" + second;
        }

        if (d > 0) {
            return d + "天" + h + "时" + m + "分";
        }
        else {
            var longTime = h > 0;
            if (longTime) {
                return front + ":" + mid;
            } else {
                return mid + ":" + back;//+ '秒';
            }
        }
    }

    public static getClockStrByS(second: number, showsecond: boolean = true, showhour: boolean = true) {
        second = Math.floor(second);
        if (second < 0) second = 0;
        var h = Math.floor(second / 3600);
        second -= h * 3600;
        var m = Math.floor(second / 60);
        second -= m * 60;
        var front = "00";
        if (h > 9) {
            front = "" + h;
        } else {
            front = "0" + h;
        }
        var mid = "00";
        if (m > 9) {
            mid = "" + m;
        } else {
            mid = "0" + m;
        }

        let str = ""
        if (showhour) {
            str += front;
            str += ":"
        }
        str += mid;

        if (showsecond)
            str += ":" + (second < 10 ? "0" : "") + second;

        return str
    }

    public static checkObjEmpty(obj: any) {
        if (obj) {
            for (var i in obj) {
                return false;
            }
            return true;
        } else {
            return true;
        }
    }

    public static checkOrderOver(orderTime: number) {
        var date = new Date(orderTime);
        var dateNow = new Date();

        if (date.getFullYear() == dateNow.getFullYear() &&
            date.getMonth() == dateNow.getMonth() &&
            date.getDate() == dateNow.getDate()
        ) {
            return false;
        } else {
            return true;
        }
    }

    public static loadBundler(name: string) {
        return new Promise((resolve, reject) => {
            assetManager.loadBundle(name, (err, ret) => {
                console.log(ret)
                resolve(null);
            })
        })
    }

    /**
     * 获取Bundle内资源路径列表
     */
    private static async _getBundleAssets(
        bundle: AssetManager.Bundle
    ): Promise<string[]> {
        try {
            let paths: string[] = [];
            //方法1：直接从配置的paths._map中获取所有键（路径）
            if(bundle['config']?.paths?._map) {//直接从配置的paths._map中获取所有键（路径）
                paths = Array.from(Object.keys(bundle['config'].paths._map));
            }else if (bundle['_assetMap']) {//方法2：兼容旧的_assetMap方式
                paths = Array.from(bundle['_assetMap'].keys());
            }else {//方法3：回退方案
                paths = bundle.getDirWithPath('').map(item => item.path);
            }
            return paths;
        } catch (error) {
            console.error(`获取Bundle资源路径失败:`, error);
            throw error;
        }
    }


    /*public static loadRes(path: string, bundle: string, type: typeof Asset, callback: any = null) {
        return new Promise((resolve, reject) => {
            if (!assetManager.getBundle(bundle)) {
                console.log(bundle);
            }
            let ret = assetManager.getBundle(bundle).get(path, type);
            if (ret) {
                if (callback) callback(null, ret);
                resolve(ret);
                return;
            }
            assetManager.getBundle(bundle).load(path, type, (err, ret: Asset) => {
                if (err) {
                    console.warn("加载错误：", path, err);
                    if (callback) callback(err, null);
                    reject(null);
                }
                else {
                    if (callback) callback(null, ret);
                    resolve(ret);
                }
            });
        })
    }*/

    public static weight(v: number[]): number {
        var mTotalWeight = 0;
        for (var i = 0; i < v.length; ++i) {
            mTotalWeight += v[i];
        }
        if (mTotalWeight <= 0) return -1;
        var randnum = Math.round(Math.random() * Number.MAX_VALUE) % mTotalWeight;
        for (var i = 0; i < v.length; ++i) {
            if (randnum < v[i]) {
                return i;
            }
            else {
                randnum -= v[i];
            }
        }
        return -1;
    }

    public static shuffle(arr: any[]) {
        for (let i = arr.length - 1; i >= 0; i--) {
            let rIndex = Math.floor(Math.random() * (i + 1));
            let temp = arr[rIndex];
            arr[rIndex] = arr[i];
            arr[i] = temp;
        }
        return arr;
    }

    /**
     * 从数组中随机选择指定数量的元素，如果超出数组长度则循环取值
     * @param array 原始数组
     * @param count 需要选择的元素数量
     * @returns 包含随机选择元素的新数组
     */
    public static getRandomElements<T>(array: T[], count: number): T[] {
        if(array.length === 0) {
            console.warn('Warning: The input array is empty.');
            return [];
        }
        //创建数组的副本以避免修改原数组
        const shuffledArray = [...array];
        //使用 Fisher-Yates Shuffle 算法进行乱序
        for(let i = shuffledArray.length - 1; i > 0; i--) {
            const j = Math.floor(Math.random() * (i + 1));
            // 交换元素
            [shuffledArray[i], shuffledArray[j]] = [shuffledArray[j], shuffledArray[i]];
        }
        //如果请求的数量大于数组长度，给出提示并循环取值
        if (count > array.length) {
            const excess = count - array.length;
            console.log(`Notice: Requested ${count} elements but array only has ${array.length}. Will cycle and return ${excess} extra elements.`);
            const result: T[] = [];
            for (let i = 0; i < count; i++) {
                result.push(shuffledArray[i % shuffledArray.length]);
            }
            return result;
        }
        //如果不超过数组长度，直接返回前count个元素
        return shuffledArray.slice(0, count);
    }

    /**
     * 扩展数组到指定长度，新增的元素从原始数组中随机选取
     * @param originalArray 原始数组（元素唯一）
     * @param targetLength 目标长度
     * @returns 扩展后的数组
     */
    public static extendArray<T>(originalArray: T[], targetLength: number): T[] {
        //如果目标长度 <= 原数组长度，直接返回原数组的浅拷贝
        if(targetLength <= originalArray.length) {
            return [...originalArray];
        }
        //创建结果数组的初始副本
        const result = [...originalArray];
        //计算需要添加的元素数量
        const elementsToAdd = targetLength - originalArray.length;
        //随机从 originalArray 选取元素并添加到 result
        for (let i = 0; i < elementsToAdd; i++) {
            //从 originalArray 中随机选择一个元素
            const randomIndex = Math.floor(Math.random() * originalArray.length);
            const randomElement = originalArray[randomIndex];
            // 将随机元素添加到结果数组中
            result.push(randomElement);
        }
        return result;
    }

    public static getDate(time: number): string {
        var now = new Date(time),
            y = now.getFullYear(),
            m = now.getMonth() + 1,
            d = now.getDate();
        return y + "-" + (m < 10 ? "0" + m : m) + "-" + (d < 10 ? "0" + d : d) + " " + now.toTimeString().substr(0, 8);
    }

    //货币进位
    public static goldCrarryBit(gold: number): string {

        var array = [
            [100000000, 'N'],
            [10000000, 'T'],
            [1000000, 'G'],
            [100000, 'M'],
            [10000, 'K'],
            [1000, 'B'],
        ];
        for (var i = 0; i < array.length; i++) {
            var value = gold / (array[i][0] as number);
            if (value > 1) {
                return '' + value.toFixed(1) + array[i][1];
            }
        }
        return gold.toString();
    }
    //定点数
    public static fixFloat(val: number, count: number = 2) {
        var a = Math.pow(10, count)
        return Math.floor(val * a) / a;
    }

    public static async flyAnim(type: ITEM_TYPE, starNode: Node, targetNode: Node, count: number, radius: number, callback: Function) {
        let getPoint = (r, ox, oy, count) => {
            var point = []; //结果
            var radians = (Math.PI / 180) * Math.round(360 / count), //弧度
                i = 0;
            for (; i < count; i++) {
                var x = ox + r * Math.sin(radians * i),
                    y = oy + r * Math.cos(radians * i);
                point.unshift(v2(x, y)); //为保持数据顺时针
            }
            return point;
        }

        let start = starNode.worldPosition;
        var array = getPoint(radius, start.x, start.y, count);
        var nodeArray = new Array();
        for (var i = 0; i < array.length; i++) {
            let ret: any = await bundleMgr.loadAsset(GameConst.bundleName.resources, `prefabs/${type}`, Prefab);
            var gold = PoolManager.getNode(ret);
            gold.setWorldScale(v3(0.7, 0.7, 0.7));
            // gold.zIndex = GameConst.WIN_LAYER.TIP;
            gold.parent = find("Canvas")
            var randPos = v3(array[i].x + Utils.getRandomInt(0, 50), array[i].y + Utils.getRandomInt(0, 50), 1);
            gold.setWorldPosition(start);
            nodeArray.push({ gold, randPos });
        }
        var notPlay = false;
        let dstPos = targetNode.worldPosition.clone();
        for (let i = 0; i < nodeArray.length; i++) {
            let pos = nodeArray[i].randPos;
            let node = nodeArray[i].gold;
            nodeArray[i].gold.id = i;
            tween(node)
                .to(0.2, { worldPosition: pos })
                .delay(i * 0.03)
                .to(0.7, { worldPosition: v3(dstPos.x, dstPos.y, 1) })
                .call(() => {
                    if(!notPlay) {
                        notPlay = true;
                        tween(targetNode)
                            .to(0.1, { scale: v3(2, 2, 2) })
                            .to(0.1, { scale: v3(1, 1, 1) })
                            .call(() => {
                                notPlay = false;
                            }).start()
                    }
                    let idx: number = node["_id"];
                    callback(idx == nodeArray.length - 1);
                    PoolManager.putNode(node);
                })
                .start()
        }
    }

    static formatString(s: string, ...arg: string[]) {

        for (var i = 0; i < arg.length; i++) {
            var reg = new RegExp("\\{" + i + "\\}", "gm");
            s = s.replace(reg, arg[i]);
        }
        return s;
    }
    // static convetOtherNodeSpaceAR(sourceNode:Node, targetNode:Node)
    // {
    //     var worldP:Vec2=sourceNode.convertToWorldSpaceAR(v2(0,0))        
    //     return targetNode.convertToNodeSpaceAR(worldP)
    // }
    static count(obj: any) {
        if (!obj) return 0;
        var num = 0;
        for (var k in obj) {
            num++;
        }
        return num;
    };
    static copy(obj: any) {
        var newObj = Object.create(obj);
        Object.assign(newObj, obj);
        return newObj;
    }


    /**
     * 数据解密
     * @param {String} str 
     */
    public static decrypt(b64Data: string) {
        if(b64Data == null || b64Data == undefined){
           return "";
        }
        let n: number = 6;
        if (b64Data.length % 2 === 0) {
            n = 7;
        }
        let decodeData = '';
        for (var idx = 0; idx < b64Data.length - n; idx += 2) {
            decodeData += b64Data[idx + 1];
            decodeData += b64Data[idx];
        }
        decodeData += b64Data.slice(b64Data.length - n + 1);
        decodeData = this._base64Decode(decodeData);
        return decodeData;
    }

    /**
     * 数据加密
     * @param {String} str 
     */
    public static encrypt(str: string) {
        if(str == null || str == undefined){
          return "";
        }
        let b64Data = this._base64encode(str);
        let n: number = 6;
        if (b64Data.length % 2 === 0) {
            n = 7;
        }
        let encodeData: string = '';
        for (let idx = 0; idx < (b64Data.length - n + 1) / 2; idx++) {
            encodeData += b64Data[2 * idx + 1];
            encodeData += b64Data[2 * idx];
        }
        encodeData += b64Data.slice(b64Data.length - n + 1);
        return encodeData;
    }

    //public method for encoding
    /**
     * base64加密
     * @param {string}input 
     * @returns 
     */
    private static _base64encode(input: string) {
        let keyStr: string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
        let output: string = "", chr1: number, chr2: number, chr3: number, enc1: number, enc2: number, enc3: number, enc4: number, i: number = 0;
        input = this._utf8Encode(input);
        while (i < input.length) {
            chr1 = input.charCodeAt(i++);
            chr2 = input.charCodeAt(i++);
            chr3 = input.charCodeAt(i++);
            enc1 = chr1 >> 2;
            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
            enc4 = chr3 & 63;
            if (isNaN(chr2)) {
                enc3 = enc4 = 64;
            } else if (isNaN(chr3)) {
                enc4 = 64;
            }
            output = output +
                keyStr.charAt(enc1) + keyStr.charAt(enc2) +
                keyStr.charAt(enc3) + keyStr.charAt(enc4);
        }
        return output;
    }

    /**
     * utf-8 加密
     * @param string 
     * @returns 
     */
    private static _utf8Encode(string: string) {
        string = string.replace(/\r\n/g, "\n");
        let utftext: string = "";
        for (let n: number = 0; n < string.length; n++) {
            let c: number = string.charCodeAt(n);
            if (c < 128) {
                utftext += String.fromCharCode(c);
            } else if ((c > 127) && (c < 2048)) {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            } else {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }
        }
        return utftext;
    }

    /**
     * utf-8解密
     * @param utftext 
     * @returns 
     */
    private static _utf8Decode(utftext: string) {
        let string = "";
        let i: number = 0;
        let c: number = 0;
        let c1: number = 0;
        let c2: number = 0;
        let c3: number = 0;
        while (i < utftext.length) {
            c = utftext.charCodeAt(i);
            if (c < 128) {
                string += String.fromCharCode(c);
                i++;
            } else if ((c > 191) && (c < 224)) {
                c2 = utftext.charCodeAt(i + 1);
                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                i += 2;
            } else {
                c2 = utftext.charCodeAt(i + 1);
                c3 = utftext.charCodeAt(i + 2);
                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                i += 3;
            }
        }
        return string;
    }

    /**
     * base64解密
     * @param {string}input 解密字符串
     * @returns 
     */
    private static _base64Decode(input: string) {
        let keyStr: string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
        let output: string = "";
        let chr1: number;
        let chr2: number;
        let chr3: number;
        let enc1: number;
        let enc2: number;
        let enc3: number;
        let enc4: number;
        let i: number = 0;
        input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
        while (i < input.length) {
            enc1 = keyStr.indexOf(input.charAt(i++));
            enc2 = keyStr.indexOf(input.charAt(i++));
            enc3 = keyStr.indexOf(input.charAt(i++));
            enc4 = keyStr.indexOf(input.charAt(i++));
            chr1 = (enc1 << 2) | (enc2 >> 4);
            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
            chr3 = ((enc3 & 3) << 6) | enc4;
            output = output + String.fromCharCode(chr1);
            if (enc3 != 64) {
                output = output + String.fromCharCode(chr2);
            }
            if (enc4 != 64) {
                output = output + String.fromCharCode(chr3);
            }
        }
        output = this._utf8Decode(output);
        return output;
    }

    /**
     * 获取是否为16:9的常规屏幕尺寸
     */
    public static isNormalScreen(): boolean {
        return  screen.windowSize.height /screen.windowSize.width < 1335 / 750;
    }
};