import Gamecfg from "../common/gameCfg";
import { gameMethod } from "../common/gameMethod";
import { KindItem, OrderList } from "../common/Xys";
import Config from "../Config";
import { ChatEvent } from "../data/const/EventConst";
import { AudioConst } from "../data/const/TypeConst";
import GameDataCenter from "../data/GameDataCenter";
import RollLabel, { RollLabelParam } from "../frameWork/compment/RollLabel";
import UIHelp from "../logic/ui/UIHelp";
import { FormulaCom } from "./Formula";
import GameMath from "./GameMath";
import { I18n } from "./I18nUtil";
import ColorAssembler2D from "./LabelColor";
import Load from "./Load";

export class BezierData {
    startPos: cc.Vec2;
    c1: cc.Vec2;  // 起点的控制点
    c2: cc.Vec2;  //终点的控制点
    endPos: cc.Vec2;
}

class UICommon {
    // 初始化
    init() { }
    setIconByKindItem(node: cc.Node, item: number[], cb?: (isSucc: boolean) => void) {

    }
    // 给特定的节点加新增一个图片节点
    setNodeChild(node: cc.Node, url: string, x: number = 0, y: number = 0, scale: number = 1) {
        if (node.getChildByName("newImgTemp")) {
            node.getChildByName("newImgTemp").active = true
            return
        }
        let child = new cc.Node()
        child.parent = node
        child.name = "newImgTemp"
        child.x = x
        child.y = y
        child.scale = scale
        Load.loadTexture(child, url)
    }
    /**设置label或富文本的文字 */
    setLabel(node: cc.Node, value: string | number) {
        if (typeof value === 'number') {
            value = value.toString();
        } else if (value == undefined) {
            value = "";
        }
        // 文本和富文本只能二选一
        if (node.getComponent(cc.RichText)) {
            let defaultColor = "#9F90A9"//node.color.toHEX('#rrggbb');
            node.getComponent(cc.RichText).string = `<color=${defaultColor}>${value}</c>`;
        } else {
            node.getComponent(cc.Label).string = value;
        }
    }

    setLibaoLabel(node: cc.Node, orderinfo: OrderList) {
        this.setLabel(node, orderinfo?.desc)
    }

    //获取价格标题
    getLibaoLabel(orderinfo: OrderList) {
        return orderinfo?.desc
    }

    setI18nLangLabel(node: cc.Node, key: string, ...args) {
        let str = I18n.getI18nLang(key, ...args)
        this.setLabel(node, str)
    }

    setI18nTextLabel(node: cc.Node, key: string, ...args: any[]) {
        let str = I18n.getI18nText(key, ...args)
        this.setLabel(node, str)
    }

    /**设置图片富文本,仅支持道具,传入itemList为道具ID,如 [1,2] */
    getImageStr(str: string, itemList: KindItem[] | any[][]): string {
        if (itemList.length == 0) {
            return str
        }
        // itemList.forEach((element, itemIndex) => {
        //     let itemCfg = GameDataCenter.item.getItemCfgBase(element)
        //     if (itemCfg) {
        //         str = str.replace(`{${itemIndex}}`, `<img src='${itemCfg.icon}'/>`)
        //     }
        // });
        return str
    }

    // /**设置图片富文本,仅支持道具,传入imageList为图片资源ID,如 [1,2]  @TODO 是否能够修改图片尺寸*/
    // setImageRt(node: cc.Node, str: string, imageList: number[] | string[]) {
    //     if (imageList.length == 0) {
    //         this.setLabel(node, str)
    //         return
    //     }
    //     let index = 0
    //     imageList.forEach(element => {
    //         Load.getTexture(`item/${element}`, (succ: boolean, asset: cc.SpriteFrame) => {
    //             index++
    //             console.log("erqasdasasdasd", index, asset)
    //             if (succ) {
    //                 node.getComponent(cc.RichText).imageAtlas["_spriteFrames"][element] = asset
    //             }
    //             if (index == imageList.length) {
    //                 console.log("erqasdasasdasd33")
    //                 imageList.forEach((element, imgIndex) => {
    //                     // <img src='emoji1'/>
    //                     str = str.replace(`{${imgIndex}}`, `<img src='${element}'/>`)
    //                 });
    //                 node.getComponent(cc.RichText).string = str
    //             }
    //         })
    //     });
    // }
    /**图片、文字 去色,0原色 1去色,递归 */
    setState(node: cc.Node, state: 0 | 1, all: boolean = false) {
        let url = state == 0 ? "2d-sprite" : "2d-gray-sprite"
        if (all) {
            let sprites = node.getComponentsInChildren(cc.Sprite)
            let labels = node.getComponentsInChildren(cc.Label)
            sprites.forEach(sprite => {
                sprite.setMaterial(0, cc.Material.getBuiltinMaterial(url))
            })
            labels.forEach(label => {
                label.setMaterial(0, cc.Material.getBuiltinMaterial(url))
            })
        } else {
            if (node.getComponent(cc.Sprite) != null) {
                node.getComponent(cc.Sprite).setMaterial(0, cc.Material.getBuiltinMaterial(url))
            }
            if (node.getComponent(cc.Label) != null) {
                node.getComponent(cc.Label).setMaterial(0, cc.Material.getBuiltinMaterial(url))
            }
        }
    }

    /** spine动画置灰 */
    setSpineState(node: cc.Node, isGrayed: boolean) {
        if (node.getComponent(sp.Skeleton) != null) {
            if (isGrayed) {
                cc.assetManager.loadBundle('materials', (err, bundle: cc.AssetManager.Bundle) => {
                    bundle.load('gray-spine', cc.Material, (err, asset) => {
                        node.getComponent(sp.Skeleton).setMaterial(0, asset);
                    });
                });
            } else {
                node.getComponent(sp.Skeleton).setMaterial(0, cc.Material.getBuiltinMaterial("2d-spine"));
            }
        };
    }

    /**按钮灰化，只有注册click事件，才会真正被禁用 */
    setBtnGrayState(node: cc.Node, isGray: boolean) {
        let button = node.getComponent(cc.Button);
        if (!button) {
            return;
        }
        button.interactable = !isGray;
        button.enableAutoGrayEffect = isGray;
    }
    /**判断按钮是否为失效状态 */
    isBtnGray(node: cc.Node): boolean {
        let button = node.getComponent(cc.Button);
        if (!button) {
            return false;
        }
        return !button.interactable;
    }
    /**节点注册事件 */
    onRegisterEvent(node: cc.Node, callback, target, params: any = [], audio: AudioConst = AudioConst.effect_click) {
        if (!node) {
            return;
        }
        let cb = callback
        if (callback == null) { return }
        callback = function (event: cc.Event.EventTouch) {
            // 判断下是否有按钮组件,有的话,如果按钮是未激活状态,则不执行回调方法
            let btn = node.getComponent(cc.Button)
            if (btn && btn.interactable == false) {
                return
            }
            cb.bind(target)(event, params)
            GameDataCenter.audio.playEffect(audio)
            event.stopPropagation()
        }
        node.on(cc.Node.EventType.TOUCH_END, callback, target)
    }

    /**节点取消注册事件 */
    unRegisterEvent(node: cc.Node) {
        if (!node) { return }
        node.off(cc.Node.EventType.TOUCH_END);
    }
    /**获取节点的世界坐标 */
    getWorldPos(node: cc.Node): cc.Vec2 {
        return node.convertToWorldSpaceAR(cc.Vec2.ZERO)
    }
    getWorldPosCenter(node: cc.Node): cc.Vec2 {
        let vec = new cc.Vec2(node.width * (0.5 - node.anchorX), node.height * (0.5 - node.anchorY))
        return node.convertToWorldSpaceAR(vec)
    }
    /**设置节点在世界坐标系下的相对坐标 */
    setWorldPos(node: cc.Node, pos: cc.Vec2) {
        let _pos = node.parent.convertToNodeSpaceAR(pos)
        node.x = _pos.x
        node.y = _pos.y
    }
    /**获取node位于target的坐标相对坐标 */
    getPositionInView(node: cc.Node, target: cc.Node): { x: number, y: number } {
        let worldPos = node.parent.convertToWorldSpaceAR(node.position);
        let viewPos = target.convertToNodeSpaceAR(worldPos);
        return viewPos;
    }
    /**通过name从根节点递归获取node */
    getTargetNodeByName(root: cc.Node, name: string) {
        if (root.name == name) {
            return root
        }
        for (let i = 0; i < root.children.length; i++) {
            const child = root.children[i];
            let targetNode = this.getTargetNodeByName(child, name)
            if (targetNode != null) {
                return targetNode
            }
        }
        return null
    }
    // 递归所有子节点并且更新节点分组
    setNodeAllGroups(node: cc.Node, group: string) {
        // 定义一个回调函数，这个函数将对每个节点执行操作
        let myCallback = function (node) {
            // 在这里处理节点
            node.group = group
        };

        // 开始递归遍历
        this.recursiveTraverseChildren(node, myCallback);
    }

    // 递归遍历所有子节点
    recursiveTraverseChildren(node: cc.Node, callback: Function) {
        // 对当前节点执行回调函数
        callback(node);

        // 遍历当前节点的所有子节点
        for (let i = 0; i < node.children.length; i++) {
            // 递归遍历子节点
            this.recursiveTraverseChildren(node.children[i], callback);
        }
    }
    // 安卓设备的适配在主activity中执行
    // ios设备,需要在游戏中修改widget
    // 返回是否为刘海,以及刘海高度
    isLiuhai(): [boolean, number] {
        let isLiuhai = false
        let height = this.getLiuhaiHeight()
        if (cc.sys.os == cc.sys.OS_IOS) {
            let _isLiuhai = jsb.reflection.callStaticMethod('SDKWrapper', 'isLiuhai:', '')
            isLiuhai = _isLiuhai == "1"
        } else if (cc.sys.os == cc.sys.OS_ANDROID) {
            let _isLiuhai = jsb.reflection.callStaticMethod("org/cocos2dx/javascript/AppActivity", "getIsLiuhai", "()Ljava/lang/String;");
            isLiuhai = _isLiuhai == "1"
        }
        return [isLiuhai, height]
    }
    /**获取屏幕高度 */
    getWinHeight() {
        return cc.winSize.height //- this.getLiuhaiHeight()
    }
    getLiuhaiHeight(): number {
        let height = 0
        try {
            if (cc.sys.os == cc.sys.OS_IOS) {
                let _height = jsb.reflection.callStaticMethod('SDKWrapper', 'getLiuhaiHeight:', '');
                height = Number(_height)
            } else if (cc.sys.os == cc.sys.OS_ANDROID) {
                height = jsb.reflection.callStaticMethod("org/cocos2dx/javascript/AppActivity", "getLiuhaiHeight", "()I");
            }
            // return height
        } catch (error) {
            // return height
        }
        return height
    }
    /**获取适配scale */
    getWidghtScale(height: number = cc.winSize.height): number {
        let defaultBili = 1920 / 1080
        let realBili = height / cc.winSize.width //高宽比
        return 1 + (realBili - defaultBili)
    }

    // //将字符转为表情
    // TransformationStr(text) {
    //     let richText = "";
    //     var arr = text.split("#");
    //     for (var i = 0; i < arr.length; i++) {
    //         if (i >= 1) {
    //             let str = arr[i].slice(0, 5)
    //             let str1 = arr[i].slice(5)
    //             if (gameMethod.isEmpty(Load.getEmoji(str))) {
    //                 richText += `#${str + str1}`
    //             } else {
    //                 richText += `<img src='${str}'/>${str1}`
    //             }
    //         } else {
    //             richText += arr[i]
    //         }
    //     }
    //     return richText
    // }

    /**原表富文本模式转换成前端富文本模式*/
    getConversionStr(text: string) {
        let tempStr = ''
        let str = ''
        let arr = text.split('');
        let isColor: boolean = false
        let isValue: boolean = true

        for (let i = 0; i < arr.length; i++) {
            if (arr[i] == '<') {
                tempStr = ''
                isColor = true
                isValue = false
            }
            if (isColor) {
                tempStr += arr[i]
            }
            if (isValue) {
                str += arr[i]
            }
            if (tempStr == '<div fontcolor=') {
                tempStr = '<color=#'
                str += tempStr
                isColor = false
                isValue = true
            }
            if (tempStr == '</div>') {
                tempStr = '</color>'
                str += tempStr
                isColor = false
                isValue = true
            }
        }

        return str
    }
    /**原表成前端*/
    getTypeStr(text: string) {
        let str = text.replace(/<div fontcolor=#/g, '<color=#')
            .replace(/<div fontcolor=/g, '<color=#')
            .replace(/<\/div>/g, '</color>')
        return str
    }

    // getEpsStrArr(eps: XyS.Eps | EpsPet) {
    //     let bak: {
    //         ek: string,
    //         num: number,

    //         ekShow: string, //名称
    //         numShow: number, //展示数值
    //         suffix: string, //后缀
    //     }[] = []

    //     //遍历配置
    //     for (const key in eps) {
    //         let arrt = gameCfg.attr.pool[`${key}_`]
    //         if (!gameMethod.isEmpty(arrt)) {
    //             switch (arrt.tip) {
    //                 case 'atk'://攻击	角色的物理攻击能力
    //                 case 'def_p'://物防	角色抵抗物理攻击的能力
    //                 case 'def_s'://法防	角色抵抗法术攻击的能力
    //                 case 'hp_max'://生命	角色的生命上限值
    //                 case 'hp'://生命	角色当前的生命值
    //                 case 'speed'://速度	决定单位在战斗中的出手频率
    //                 case 'def'://防御	角色抵抗物理和法术攻击的能力
    //                     //数值直接展示
    //                     bak.push({
    //                         ek: arrt.tip,
    //                         num: eps[arrt.tip],

    //                         ekShow: arrt.name, //名称
    //                         numShow: eps[arrt.tip], //展示数值
    //                         suffix: '%', //后缀
    //                     })
    //                     break;
    //                 case 'hit_rate'://命中	成功造成伤害的概率
    //                 case 'dodge_rate'://闪躲	躲避伤害的概率
    //                 case 'crit_rate'://暴击率	攻击方产生暴击的概率
    //                 case 'crit_ratio'://暴伤	暴击产生时，伤害的变化比例
    //                 case 'hit_magic'://控制	增强技能效果命中的比例
    //                 case 'dodge_magic'://抗控	抵抗技能效果命中的比例
    //                 case 'tenacity'://抗暴	受击方被暴击概率降低
    //                 case 'atk_per'://攻击	百分比增加单位的攻击力
    //                 case 'def_per'://防御	百分比增加单位的物理防御和法术防御
    //                 case 'hp_max_per'://生命	百分比增加单位的生命上限
    //                 case 'dam'://伤害加成	千分比增加造成的伤害
    //                 case 'res'://免伤	千分比减少受到的伤害
    //                 case 'cure'://治疗	千分比增减治疗别人的效果
    //                 case 'be_cure'://受疗	千分比增减受到的治疗效果
    //                 case 'dam_p'://物伤	千分比增加造成的物理伤害
    //                 case 'dam_s'://法伤	千分比增加造成的法术伤害
    //                 case 'res_p'://物免	千分比减少受到的物理伤害
    //                 case 'res_s'://法免	千分比减少受到的法术伤害
    //                 case 'speed_per'://速度	决定单位在战斗中的出手频率
    //                 case 'def_p_per'://物防	千分比增加单位的物理防御
    //                 case 'def_s_per'://法防	千分比增加单位的法术防御
    //                 case 'toughness'://韧性	千分比减免持续伤害类负面效果带来的伤害
    //                     //数值百分比加成
    //                     bak.push({
    //                         ek: arrt.tip,
    //                         num: eps[arrt.tip],

    //                         ekShow: arrt.name, //名称
    //                         numShow: eps[arrt.tip] / 10, //展示数值
    //                         suffix: '%', //后缀
    //                     })
    //                     break;
    //             }
    //         }
    //     }

    //     return bak
    // }

    /** 打字效果 */
    typingAni(label: cc.Label | cc.RichText, text: string, time: number = 1 / 60, cb: Function = () => { }) {
        if (!text || text.length <= 0) {
            cb()
            return
        }

        let html = '';
        let tempStr = ''
        let tempColorStr = ''
        let str: string[] = []
        let arr = text.split('');
        let len = arr.length;
        let step = 0;
        let isColor: boolean = false
        let isValue: boolean = false

        if (label instanceof cc.RichText) {
            for (let i = 0; i < len; i++) {
                if (arr[i] == '<') {
                    tempColorStr = ''
                    isValue = false
                }

                if (isValue) {
                    tempStr = `${tempColorStr}${arr[i]}</color>`
                    str.push(tempStr)
                    tempStr = ''
                }

                if (arr[i] == '#') {
                    isColor = true
                }

                if (arr[i] == '>' && tempColorStr != '') {
                    isColor = false
                    isValue = true
                    tempColorStr = `<color=${tempColorStr}>`
                }
                if (isColor) {
                    tempColorStr += arr[i]
                }
            }

            arr = str
            len = str.length
        }

        let typingFunc = () => {
            if (step >= len) {
                label.unschedule(typingFunc);
                cb && cb();
            } else {
                html += arr[step];
                label.string = html
            }
            step++
        }
        label.schedule(typingFunc, time, len)
    }
    /** 富文本打字效果 */
    typingRich(label: cc.RichText, text: string, wordsNum: number, time: number = 1 / 60, cb: Function = () => { }) {
        if (!text || text.length <= 0) {
            cb()
            return
        }
        let str = text;
        let charArr = str.replace(/<.+?\/?>/g, '').split('');
        let tempStrArr = [str],
            curstr = str;
        for (let i = charArr.length; i > 1; i--) {
            let lastIdx = curstr.lastIndexOf(charArr[i - 1]);
            let prevstr = curstr.slice(0, lastIdx);
            let nextstr = curstr.slice(lastIdx + 1, curstr.length);
            if ((i - 1) % wordsNum == 0) tempStrArr.push(prevstr + nextstr);
            curstr = prevstr + nextstr
        }
        let step = 0,
            len = tempStrArr.length;
        let typingFunc = () => {
            if (step >= len) {
                label.unschedule(typingFunc);
                cb && cb();
            } else {
                label.string = tempStrArr.pop()
            }
            step++
        }
        label.schedule(typingFunc, time, len)
        typingFunc()
    }
    // /** 
    //  * @description:给需要点击回调的RichText添加回调脚本
    //  * @param {type} node 包含RichText组件的Node
    //  * @param {type} component 需要处理点击事件的脚本
    //  * @return {type}
    //  */
    // createRichTextCallback(node, component) {
    //     if (gameMethod.isEmpty(node)) {
    //         console.log("node must be cc.Node!!!");
    //         return;
    //     }
    //     let richText = node.getComponent(cc.RichText);
    //     if (gameMethod.isEmpty(richText)) {
    //         console.log("node must have RichText component!!!");
    //         return;
    //     }
    //     let st = node.addComponent(RichTextEvent);
    //     st.setObject(component);
    // }
    // /** 
    //  * @description:获取技能buff描述
    //  * @param {type} buffId 需要处理点击事件的脚本
    //  * @return {type}
    //  */
    // getBuffDesc(buffId: string, withTitle: boolean = true): string {
    //     let conf = gameCfg.skill_all_buff.getItem(buffId)
    //     if (conf == null) {
    //         console.log(" buff info is not exist!!!");
    //         return ""
    //     }
    //     if (withTitle) {
    //         return `<color=#24B6CB>[${conf.name}]</c>\n${conf.desc}`
    //     }
    //     return conf.desc
    // }

    //给 spine 重新拷贝一份 skeletonData 数据，让他们不重复
    public copySpine(skeleton: sp.Skeleton) {
        let date = new Date();
        // 记录当前播放的动画
        const animation = skeleton.animation
        const spdata = skeleton.skeletonData;
        let copy = new sp.SkeletonData();
        cc.js.mixin(copy, spdata);
        // @ts-ignore
        copy._uuid = spdata._uuid + "_" + date.getTime() + "_copy";
        let old = copy.name;
        let newName = copy.name + "_copy";
        copy.name = newName;
        copy.atlasText = copy.atlasText.replace(old, newName);
        // @ts-ignore
        copy.textureNames[0] = newName + ".png";
        // @ts-ignore
        copy.init && copy.init();
        skeleton.skeletonData = copy;
        // 继续播放的动画，不然会停止
        skeleton.setAnimation(0, animation, true);
    }

    /**
         * 用外部图片局部换装
         * @param skeleton   骨骼动画
         * @param slotName  需要替换的插槽名称
         * @param texture   外部图片
         */
    public changeSlot(skeleton: sp.Skeleton, slotName: string, texture: cc.Texture2D) {
        if (cc.sys.isBrowser) {
            const slot: sp.spine.Slot = skeleton.findSlot(slotName);
            const attachment: sp.spine.RegionAttachment = slot.getAttachment() as sp.spine.RegionAttachment;
            if (!slot || !attachment) {
                cc.error('updatePartialSkin')
                return;
            }
            // @ts-ignore
            const skeletonTexture = new sp.SkeletonTexture({});
            skeletonTexture.setRealTexture(texture);

            let region: sp.spine.TextureAtlasRegion = attachment.region as sp.spine.TextureAtlasRegion;
            region.u = 0;
            region.v = 0;
            region.u2 = 1;
            region.v2 = 1;
            region.width = texture.width * 5;
            region.height = texture.height * 5;
            region.originalWidth = texture.width * 5;
            region.originalHeight = texture.height * 5;
            region.rotate = false;
            region.texture = skeletonTexture;

            attachment.width = texture.width * 5;
            attachment.height = texture.height * 5;
            attachment.region = region;
            attachment.setRegion && attachment.setRegion(region)
            attachment.updateOffset && attachment.updateOffset();

            // attachment.updateUVs && attachment.updateUVs(attachment);

            slot.setAttachment(attachment);

        } else {
            // @ts-ignore
            const jsbTex = new middleware.Texture2D();
            jsbTex.setPixelsHigh(texture.height * 5);
            jsbTex.setPixelsWide(texture.width * 5);
            jsbTex.setNativeTexture(texture.getImpl());
            // @ts-ignore
            skeleton.updateRegion(slotName, jsbTex);
        }
        //  skeleton 如果使用了缓存模式则需要刷新缓存
        skeleton.invalidAnimationCache();
    }

    // 移除所有子节点
    destoryAllChildren(node: cc.Node) {
        node.children.forEach(child => {
            child.destroy()
        })
    }

    // 根据品质获取颜色
    getColorByPz(pinzhi: number | string) {
        // 品质1·8的，9瑶光的自己拼
        switch (Number(pinzhi)) {
            case 1:
                return '#B3B3B3'
            case 2:
                return '#81B58B'
            case 3:
                return '#7CACCF'
            case 4:
                return '#A77CC5'
            case 5:
                return '#E6A76A'
            case 6:
                return '#E7C45A'
            case 7:
                return '#D45F59'
            case 8:
                return '#76DEE4'
            case 9:
                return '#EB91F0'
            case 10:
                return '#FF66D9'
            case 11:
                return '#98F22E'
            case 12:
                return '#FFCC19'
            default:
                return '#FFFFFF'
        }
    }

    //  // 根据品质获取颜色
    //  setColorTxtByPz(txt:string,pinzhi: number | string) {
    //     let color = this.getColorByPz(pinzhi);
    //     let str = `[color=${color}]`
    // }

    // 根据品质获取名称
    getNameByPz(pinzhi: number | string) {
        return I18n.getI18nLang(`zhenfa_pinzhi_name_${pinzhi}`);
    }

    getEnoughColor(isenough: boolean) {
        return isenough ? "6fdf89" : "E45849"
    }

    // 修正文字布局
    fixName(msg: string): string {
        // if (msg.length >= 4) {
        //     return msg
        // }
        // let out = ""
        // for (let index = 0; index < msg.length; index++) {
        //     out += msg[index]
        //     // 添加空格
        //     if (index + 1 == msg.length) {
        //         break
        //     }
        //     if (msg.length == 2) {
        //         out += "       "
        //     } else if (msg.length == 3) {
        //         out += "  "
        //     }
        // }

        // return out;
        return msg;
    }

    // cocos画布的节点位置信息转化为微信画布位置信息
    getWxBtnPos(node: fgui.GObject): { left: number, top: number, width: number, height: number } {
        let visibleSize = cc.view.getVisibleSize();
        // console.log("==visibleSize==", visibleSize.width, visibleSize.height)
        // console.log("==position==", node.node.position)
        // let worldPos = FormulaCom.getWorldPos(node.node)
        let worldPos = fgui.GRoot.inst.localToGlobal(node.x, node.y)
        // console.log("==fgui worldPos==", fgui.GRoot.inst.localToGlobal(node.x, node.y))
        // console.log("==cc worldPos==", FormulaCom.getWorldPos(node.node))
        //获取系统信息
        let wx_size = wx?.getSystemInfoSync();
        // console.log("==wx_size==", wx_size.screenWidth, wx_size.screenHeight)
        //计算实际大小和可见区域尺寸的比例（这里以宽度为准）
        let size_scale_width = wx_size.screenWidth / visibleSize.width;
        let size_scale_height = wx_size.screenHeight / Config.realHeight;
        //计算创建用户信息按钮需要的属性,考虑锚点  
        let offsetX = node.width * node.scaleX * (node.node.anchorX / 1)
        let offsetY = node.height * node.scaleY * ((1 - node.node.anchorY) / 1)
        // console.log("==anchorX==", node.node.anchorX)
        // console.log("==anchorY==", node.node.anchorY)
        // console.log("==offsetX==", offsetX)
        // console.log("==offsetY==", offsetY)
        let left = (worldPos.x - offsetX) * size_scale_width  // (this.ui.btnUser.x + visibleSize.width / 2 - this.ui.btnUser.width / 2) * size_scale_width;
        let top = (worldPos.y + node.height / 2) * size_scale_height  //fgui
        // let top = wx_size.screenHeight - (worldPos.y - offsetY - Config.safeAreaRect.y / 2 - node.height / 2) * size_scale_height  //cc
        let width = node.width * size_scale_width * node.scaleX
        let height = node.height * size_scale_height * node.scaleY
        // let y = (Math.abs(this.ui.btnUser.y) - this.ui.btnUser.height / 2) * size_scale_width;
        // let width = this.ui.btnUser.width * size_scale_width;
        // let height = this.ui.btnUser.height * size_scale_width;
        // console.log("==left==", left)
        // console.log("==top==", top)
        // console.log("==width==", width)
        // console.log("==height==", height)
        return { left: left, top: top, width: width, height: height }
    }

    btnShake(node: cc.Node) {
        let tween = cc.tween(node)
            .delay(2)
            .to(0.05, { angle: -15 })
            .to(0.1, { angle: 15 })
            .to(0.1, { angle: -15 })
            .to(0.1, { angle: 15 })
            .to(0.05, { angle: 0 })
        cc.tween(node).repeatForever(tween).start()
    }

    /**
     * 抛物线飞行 //开始坐标 //中间坐标(高度) //结束坐标
     * p0是起始点
     * p1是中点
     * p2是终点
     * p1通过p2和p0的Xaxis相减获得
     * height是p0加上的高度
     * t 的取值范围是0-1（整个过程的归一化
     */
    paowuxian(parm: {
        node: cc.Node;
        s: { x: number; y: number };
        e: { x: number; y: number };
        h: number;
        tadd?: number; //时间加减速比例 默认100
        cbk?: Function;
    }) {
        //中间点
        let mv2 = cc.v2(parm.s.x + (parm.e.x - parm.s.x) / 2, (parm.s.y + parm.e.y) / 2 + parm.h);
        //设置时间
        let time = Math.sqrt(Math.abs(mv2.y - parm.s.y)) + Math.sqrt(Math.abs(mv2.y - parm.e.y));
        time /= 50;
        if (parm.tadd != null) {
            time = (time * parm.tadd) / 100;
        }
        //起始点
        parm.node.x = parm.s.x;
        parm.node.y = parm.s.y;
        cc.tween(parm.node)
            .bezierTo(time, mv2, mv2, cc.v2(parm.e.x, parm.e.y))
            .call(() => {
                if (parm.cbk != null) {
                    parm.cbk();
                }
            })
            .start();
    }

    //抛物线 + 落地弹弹弹
    paowuxian_tan(
        parm: {
            node: cc.Node;
            s: { x: number; y: number };
            e: { x: number; y: number };
            h: number;
            jian: number; //弹的衰减比例 100 不衰减
            tadd?: number; //时间加减速比例 默认100
            cbk?: Function;
        },
        t?: number //当前第几次弹 初次是0 不算弹的
    ) {
        //跳
        this.paowuxian({
            node: parm.node,
            s: parm.s,
            e: parm.e,
            h: parm.h,
            tadd: parm.tadd,
            cbk: () => {
                //第几次弹
                if (t == null) {
                    t = 0;
                }
                t += 1;
                //当前衰减比例
                let pers = Math.pow(parm.jian / 100, t);
                if (pers < 0.1) {
                    //不再弹了
                    if (parm.cbk != null) {
                        parm.cbk();
                        return;
                    }
                }
                //目标
                let _h = parm.h * pers; //跳的高度 衰减
                let _e = {
                    x: parm.e.x + (parm.e.x - parm.s.x) * pers, //跳的宽度 衰减
                    y: parm.e.y, //水平坐标不变
                };
                //调用自己
                this.paowuxian_tan(
                    {
                        node: parm.node,
                        s: parm.e, //开始 = 上一次的结束
                        e: _e,
                        h: _h,
                        jian: parm.jian,
                        tadd: parm.tadd,
                        cbk: parm.cbk,
                    },
                    t
                );
            },
        });
    }

    // 引擎自带的贝塞尔曲线运动
    runBezierAct(actNode: cc.Node, duration: number, bezierDatas: BezierData[]) {
        if (bezierDatas.length <= 0) return
        let tw = cc.tween()
        for (let i = 0; i < bezierDatas.length; ++i) {
            tw.bezierTo(duration, bezierDatas[i].c1, bezierDatas[i].c2, bezierDatas[i].endPos)
        }
        actNode.setPosition(bezierDatas[0].startPos)
        tw.clone(actNode).start()
    }

    // 匀速贝塞尔曲线运动
    runUniformBezierAct(actNode: cc.Node, duration: number, bezierDatas: BezierData[], onComplete?: Function) {
        if (bezierDatas.length <= 0) return

        let tw = cc.tween()
        let allBezierPos: cc.Vec2[] = []
        for (let i = 0; i < bezierDatas.length; ++i) {
            let posArr = [bezierDatas[i].startPos, bezierDatas[i].c1, bezierDatas[i].c2, bezierDatas[i].endPos]
            allBezierPos = allBezierPos.concat(this._caculateBezierPoint(posArr))
        }

        let totalLineLen = this._caculateBezierLength(allBezierPos)
        let speed = totalLineLen / duration
        let isCanBezier = false
        for (let i = 1; i < allBezierPos.length; ++i) {
            let dis = cc.v2(allBezierPos[i].x - allBezierPos[i - 1].x, allBezierPos[i].y - allBezierPos[i - 1].y).len()
            if (dis > 0) {
                // 这里过滤掉两段贝塞尔曲线首位连接的点
                let t = dis / speed
                tw.to(t, { position: allBezierPos[i] })
                    .call(() => {
                        let angle = FormulaCom.getAngleByPos(allBezierPos[i - 1].x, allBezierPos[i - 1].y, allBezierPos[i].x, allBezierPos[i].y)
                        // let angle = FormulaCom.getAngle(allBezierPos[i - 1],allBezierPos[i])
                        // console.log("angle = ",angle)
                        actNode.angle = angle
                    })
                isCanBezier = true
            }
        }
        if (!isCanBezier) {
            console.error("allBezierPos.length == 0")
            return
        }
        actNode.setPosition(allBezierPos[0])
        tw.clone(actNode).call(() => {
            onComplete && onComplete();
        }).start()
    }

    // 计算所有贝塞尔曲线的点
    private _caculateBezierPoint(posArr: cc.Vec2[]) {
        let allBezierPos = []
        let allDis = cc.Vec2.distance(posArr[0], posArr[3])
        let gapCount = Math.ceil(allDis / 10)//距离5像素为一点
        let gap = 1 / gapCount // 每次迭代步长,这个值越小越精细,但是效率越低,这里迭代300次已经够了
        for (let i = 0; i <= 1; i += gap) {
            let pos = this._caculateBezierP(posArr, i)
            allBezierPos.push(pos)
        }

        return allBezierPos
    }

    // 计算贝塞尔曲线的长度
    private _caculateBezierLength(allBezierPos: cc.Vec2[]) {
        let totalLineLen = 0
        for (let i = 1; i < allBezierPos.length; ++i) {
            let dis = cc.v2(allBezierPos[i].x - allBezierPos[i - 1].x, allBezierPos[i].y - allBezierPos[i - 1].y).len()
            totalLineLen += dis
        }

        return totalLineLen
    }

    /**
     * 计算三阶贝塞尔在 t时刻 的位置
     * @param p 三阶贝塞尔的四个点,数组对应为 0起点,1起点控制点,2终点控制点,3终点
     * @param t 传入0-1的值,一个时间的迭代过程
     * @returns 
     */
    private _caculateBezierP(p: cc.Vec2[], t: number): cc.Vec2 {
        // 三阶贝塞尔运算
        let bezierP: cc.Vec2 = cc.v2()
        bezierP.x = Math.floor(Math.pow(1 - t, 3) * p[0].x + 3 * t * Math.pow(1 - t, 2) * p[1].x + 3 * Math.pow(t, 2) * (1 - t) * p[2].x + Math.pow(t, 3) * p[3].x);
        bezierP.y = Math.floor(Math.pow(1 - t, 3) * p[0].y + 3 * t * Math.pow(1 - t, 2) * p[1].y + 3 * Math.pow(t, 2) * (1 - t) * p[2].y + Math.pow(t, 3) * p[3].y);
        return bezierP;
    }

    /** 判断隐藏礼包是否开启
     * @param ycgift 隐藏礼包数据
     * @param dc 档次
     * @param buyNum 购买次数
     * @param buyCons 购买金额
     */
    checkYcGiftOpen(tiaojian: number[], buyNum: number, buyCons: number) {
        if (gameMethod.isEmpty(tiaojian)) return false;
        //有配置全局充值金额并且全局充值大于固定金额显示
        if (!gameMethod.isEmpty(tiaojian[2]) && GameDataCenter.sevBack?.userInfo?.a?.iscz >= tiaojian[2]) { return true; }
        // 隐藏礼包条件判断
        if (tiaojian[0] > 0 && tiaojian[1] <= 0) {
            // 第一种 只判断次数
            if (buyNum >= tiaojian[0]) { return true; }
        } else if (tiaojian[0] <= 0 && tiaojian[1] > 0) {
            // 第二种 只判断金额
            if (buyCons >= tiaojian[1]) { return true; }
        } else if (tiaojian[0] > 0 && tiaojian[1] > 0) {
            // 第三种 判断次数和金额 满足一种
            if (buyNum >= tiaojian[0] || buyCons >= tiaojian[1]) { return true; }
        }

        return false;
    }

    // 获取属性名跟值       *传入值 "atk":10  返回 [攻击, 10]
    getEpsNameValue(key: string, value: number): string[] {
        let epsCfg = Gamecfg.userEp.getItem(key);
        value = value ? value : 0;
        if (gameMethod.isEmpty(epsCfg)) {
            // console.log('配置错误===', key)
            return [key, value + ""];
        }
        // return [this.fixName(epsCfg.name), (epsCfg.isPer ? `${value / 100}%` : value + "") || "0"];
        return [this.fixName(I18n.getUserEpName(epsCfg.key)), epsCfg.isPer ? `${value / 100}%` : value + ""]

    }
    /**
    * UI节点转换到目标节点下的坐标
    * @param node 节点
    * @param targetNode 目标节点
    * @returns {转换后的坐标的点|Point}
    */
    transPos(node: cc.Node, targetNode: cc.Node): cc.Vec2 {
        //转世界坐标
        var endGlobalPos = this.getWorldPos(node);
        if (!endGlobalPos) return null;
        //再转局部坐标
        var endPos = targetNode.convertToNodeSpaceAR(endGlobalPos);
        return endPos;
    }

    // 剪贴板复制功能
    public webCopyString(str: string) {
        // console.log('复制ing');

        var input = str + '';
        const el = document.createElement('textarea');
        el.value = input;
        el.setAttribute('readonly', '');
        // el.style.contain = 'strict';
        el.style.position = 'absolute';
        el.style.left = '-9999px';
        el.style.fontSize = '12pt'; // Prevent zooming on iOS

        const selection = getSelection();
        var originalRange = null;
        if (selection.rangeCount > 0) {
            originalRange = selection.getRangeAt(0);
        }
        document.body.appendChild(el);
        el.select();
        el.selectionStart = 0;
        el.selectionEnd = input.length;

        var success = false;
        try {
            success = document.execCommand('copy');
        } catch (err) { }

        document.body.removeChild(el);

        if (originalRange) {
            selection.removeAllRanges();
            selection.addRange(originalRange);
        }
        if (success) {
            UIHelp.ShowTips(I18n.getI18nText('common_copy_sucess'));
        } else {
            UIHelp.ShowTips(I18n.getI18nText('common_copy_fail'));
        }
        return success;
    }

    /**
     * 设置滚动变化的数字文本,不要再任何虚拟列表内使用
     * @param obj 需要控制的文本组件
     * @param num 变化的目标数字
     * @param cb 实时变化的数字值，自己在内部写需要执行和改变的文本（如果不传则直接对obj.text或这obj.title赋值
     * @param param 参数
     */
    SetLabelRoll(obj: fairygui.GTextField | fairygui.GLabel | fairygui.GRichTextField, num: number, cb: (val: number) => void = null, param: RollLabelParam = null) {
        let rolllabel = obj.node.getComponent(RollLabel)
        if (rolllabel == null) {
            rolllabel = obj.node.addComponent(RollLabel)
            rolllabel.obj = obj
        }
        rolllabel.setData(num, cb, param)
    }

    /**
     * 上漂文本
     * @param func 改变文本图标的方法 
     * @param node 位置节点定位用
     * @param view 挂载上漂节点用的页面
     * @param cache 缓存
     * @param randomX 
     * @param randomY 
     * @param pac 
     * @param popItem 
     */
    PopTips(func: (obj: fairygui.GLabel) => void, node: fairygui.GComponent, view: fairygui.GComponent, cache: PopTipsCache[], randomX: number[] | number = null, randomY: number[] | number = null, pac: string = "Common", popItem: string = "PopTips") {
        let data: PopTipsCache = null
        for (let i of cache) {
            if (i.obj.visible == false) {
                data = i
                break
            }
        }

        if (data == null) {
            let obj = fgui.UIPackage.createObject(pac, popItem) as fairygui.GLabel
            view.addChild(obj)
            data = new PopTipsCache(obj)
            cache.push(data)
        }
        if (typeof randomX == "number") {
            data.obj.x = node.x + randomX
        } else if (randomX) {
            let left = randomX[0]
            let right = randomX[1]
            let randomposx = GameMath.getRandomNum(left, right)
            data.obj.x = node.x + randomposx
        } else {
            data.obj.x = node.x
        }
        if (typeof randomY == "number") {
            data.obj.y = node.y + randomY
        } else if (randomY) {
            let left = randomY[0]
            let right = randomY[1]
            let randomposy = GameMath.getRandomNum(left, right)
            data.obj.y = node.y + randomposy
        } else {
            data.obj.y = node.y
        }
        func(data.obj)
        data.startTween()
    }

    /** 数字转中文 */
    NumToChinese(num) {
        if (num < 0 || num > 100) {
            return "输入的数字必须在0到100之间";
        }

        const units = ['', '十', '百', '千']; // 可以根据需要扩展  
        const chineseNums = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
        let str = '';
        let digit;

        // 处理个位数  
        digit = num % 10;
        if (digit !== 0) {
            str = chineseNums[digit];
        }

        // 处理十位数  
        num = Math.floor(num / 10);
        if (num !== 0) {
            if (num == 1) {
                str = '十' + str;
            } else {
                str = chineseNums[num] + '十' + str;
            }
        }

        // 如果需要处理更大的数字，可以继续添加逻辑  

        // 移除开头的'零'  
        str = str.replace(/^零+/, '');
        if (str === '') {
            str = '零';
        }

        return str;
    }

    /** 圆上指定角度一点 */
    GetPointOnCircle(radius: number, degree: number, center: cc.Vec2): cc.Vec2 {
        let angle = this.DegreeToAngle(degree);
        // 使用三角函数计算圆上该角度对应的点的x和y坐标  
        const x = center.x + radius * Math.cos(angle);
        const y = center.y + radius * Math.sin(angle);

        // 返回该点的坐标  
        return cc.v2(x, y);
    }

    /** 圆上任意一点 */
    GetRandomPointOnCircle(radius: number, centerX = 0, centerY = 0): cc.Vec2 {
        // 生成一个0到2π之间的随机角度（弧度）  
        const angle = Math.random() * 2 * Math.PI;

        // 使用三角函数计算圆上该角度对应的点的x和y坐标  
        const x = centerX + radius * Math.cos(angle);
        const y = centerY + radius * Math.sin(angle);

        // 返回该点的坐标  
        return cc.v2(x, y);
    }

    /** 圆上角度范围一点 */
    GetRandomPointOnAngle(radius: number, rangeAngle: number, center: cc.Vec2, point: cc.Vec2): cc.Vec2 {
        // 生成一个0到2π之间的随机角度（弧度）  
        // const angle = Math.random() * 2 * Math.PI;

        const dx = point.x - center.x;
        const dy = point.y - center.y;
        const angleRadians = Math.atan2(dy, dx);
        let angle = this.AngleToDegree(angleRadians);
        let newAngle = GameMath.getRandomNum(rangeAngle, rangeAngle * 2) - rangeAngle;
        angle += newAngle;
        let degree = this.DegreeToAngle(angle);
        // 使用三角函数计算圆上该角度对应的点的x和y坐标  
        const x = center.x + radius * Math.cos(degree);
        const y = center.y + radius * Math.sin(degree);

        // 返回该点的坐标  
        return cc.v2(x, y);
    }

    /** 圆上指定角度一点 */
    GetCirclePointOnAngle(radius: number, diffAngle: number, center: cc.Vec2, point: cc.Vec2): cc.Vec2 {
        // 生成一个0到2π之间的随机角度（弧度）  
        // const angle = Math.random() * 2 * Math.PI;

        const dx = point.x - center.x;
        const dy = point.y - center.y;
        const angleRadians = Math.atan2(dy, dx);
        let angle = this.AngleToDegree(angleRadians);
        let degree = angle + diffAngle;
        let newAngle = this.DegreeToAngle(degree);
        // 使用三角函数计算圆上该角度对应的点的x和y坐标  
        const x = center.x + radius * Math.cos(newAngle);
        const y = center.y + radius * Math.sin(newAngle);

        // 返回该点的坐标  
        return cc.v2(x, y);
    }

    /** 圆上分割角度后的坐标点 */
    GetCirclePointDivideAngle(radius: number, divideCount: number, center: cc.Vec2): cc.Vec2[] {
        // 生成一个0到2π之间的随机角度（弧度）  
        let per = divideCount / divideCount;
        let posArr = [];
        for (let i = 0; i < divideCount; i++) {
            let degree = per * i;
            // 使用三角函数计算圆上该角度对应的点的x和y坐标  
            const x = center.x + radius * Math.cos(degree);
            const y = center.y + radius * Math.sin(degree);
            posArr.push(cc.v2(x, y))
        }
        return posArr;
    }

    /** 圆上指定弧长的点 */
    GetCircleAngleLenPoint(radius: number, len: number, center: cc.Vec2, point: cc.Vec2, index: number): cc.Vec2 {
        const dx = point.x - center.x;
        const dy = point.y - center.y;
        const angleRadians = Math.atan2(dy, dx);
        let degree = this.AngleToDegree(angleRadians);
        let totalLen = 2 * Math.PI * radius;
        let needDegree = len / totalLen * 360;//弧长对应角度
        let newDegree = degree + index * needDegree;
        let newAngle = this.DegreeToAngle(newDegree);
        // 使用三角函数计算圆上该角度对应的点的x和y坐标  
        const x = center.x + radius * Math.cos(newAngle);
        const y = center.y + radius * Math.sin(newAngle);
        // 返回该点的坐标  
        return cc.v2(x, y);
    }

    /** 弧度转角度 */
    public AngleToDegree(angleRadians) {
        const angleDegrees = angleRadians * (180 / Math.PI);
        return angleDegrees
    }

    /** 角度转弧度 */
    public DegreeToAngle(degree: number) {
        const angle = degree / (180 / Math.PI);
        return angle;
    }

    /** 获取两点角度 */
    public GetTwoPointAngle(center: cc.Vec2, point: cc.Vec2) {
        const dx = point.x - center.x;
        const dy = point.y - center.y;
        const angleRadians = Math.atan2(dy, dx);
        let angle = this.AngleToDegree(angleRadians);
        return angle;
    }

    /** 求多个点的中心点 */
    public CalculateCentroid(points: cc.Vec2[]): cc.Vec2 {
        // 初始化x和y的总和  
        let sumX = 0;
        let sumY = 0;

        // 遍历所有点，累加x和y的坐标  
        points.forEach(point => {
            sumX += point.x;
            sumY += point.y;
        });

        // 计算平均x和y坐标  
        let centroidX = sumX / points.length;
        let centroidY = sumY / points.length;

        // 返回中心点坐标  
        return cc.v2(centroidX, centroidY);
    }
}

export class PopTipsCache {
    obj: fairygui.GLabel
    tween1: cc.Tween;
    tween2: cc.Tween;

    constructor(obj: fairygui.GLabel) {
        this.obj = obj


    }

    startTween() {
        this.obj.alpha = 0
        this.obj.visible = true
        let y = this.obj.y - 120;
        this.tween1 = cc.tween(this.obj).to(1, {
            y: y,
        }).call(() => {
            this.obj.visible = false
        })
        this.tween2 = cc.tween(this.obj).to(0.2, {
            alpha: 1,
        }).delay(0.6).to(0.2, {
            alpha: 0,
        })
        this.tween1.start()
        this.tween2.start()
    }

    clearTween() {
        this.tween1?.stop()
        this.tween2?.stop()
        this.obj.visible = false;
    }

}

export let uiCommon = new UICommon()