class Emitter {
    static instance;
    static getInstance() {
        if (!Emitter.instance) {
            Emitter.instance = new Emitter();
        }
        return Emitter.instance;
    }
    _name: any;
    _callbacks: any;
    constructor(name = "") {
        this._name = name;
        this._callbacks = {};
        return this;
    }
    clear() {
        this._callbacks = {};
    }
    on(event, fn) {
        if (!this._callbacks[event]) this._callbacks[event] = [];
        this._callbacks[event].push(fn);
    };
    once(event, fn) {
        function on() {
            this.off(event, on);
            fn.apply(this, arguments);
        }
        on.fn = fn;
        this.on(event, on);
    };
    off(event, fn) {
        // all
        if (0 == arguments.length) {
            this._callbacks = {};
            return;
        }

        // specific event
        let callbacks = this._callbacks[event];
        if (!callbacks) return;

        // remove all handlers
        if (1 == arguments.length) {
            delete this._callbacks[event];
            return;
        }

        // remove specific handler
        let cb;
        for (let i = 0; i < callbacks.length; i++) {
            cb = callbacks[i];
            if (cb === fn || cb.fn === fn) {
                callbacks.splice(i, 1);
                break;
            }
        }
        return;
    }
    emit(event, ...args) {
        this._callbacks = this._callbacks || {};
        // let args = [].slice.call(arguments, 1); 
        let callbacks = this._callbacks[event];
        if (callbacks) {
            callbacks = callbacks.slice(0);
            for (let i = 0, len = callbacks.length; i < len; ++i) {
                callbacks[i].apply(this, args);
            }
        }
    }
    listeners(event) {
        this._callbacks = this._callbacks || {};
        return this._callbacks[event] || [];
    };
    hasListeners(event) {
        return !!this.listeners(event).length;
    };
}
class Signal<T = void> {
    private handlers: ((data: T) => void)[] = [];
    private thisArgs: any[] = [];

    public on(handler: (data: T) => void, thisArg: any): void {
        this.off(handler);
        this.handlers.push(handler);
        this.thisArgs.push(thisArg);
    }
    public off(handler: (data: T) => void): void {
        const index: number = this.handlers.indexOf(handler);
        if (index == -1) return;
        this.handlers.splice(index, 1);
        this.thisArgs.splice(index, 1);
    }

    public emit(data: T): void {
        // protect from trigger >> off
        const handlers: ((data: T) => void)[] = [...this.handlers];
        const thisArgs: any[] = [...this.thisArgs];

        for (let i = 0; i < handlers.length; i++) {
            handlers[i].call(thisArgs[i], data);
        }
    }
}

class TimeCtrl {
    private current: number;
    constructor() {
        this.current = 0;
    }
    fillTime(time) {
        this.current += (time * 1000)
        return this;
    }
    setTime(time) {
        this.current += (time * 1000)
        return this;
    }
    wait() {
        return new Promise((resolve, reject) => {
            let lastTime = new Date().getTime();
            let id = setInterval(() => {
                let nowTime = new Date().getTime();
                this.current -= (nowTime - lastTime);
                lastTime = nowTime;
                if (this.current <= 0) {
                    clearInterval(id);
                    resolve(true);
                }
            }, 1)
        });
    }
    reset() {
        this.current = 0;
        return this;
    }
}
let pg = {
    logger: {
        d: function (str) {
            cc.log(str);
            return null;
        },
        i: function (str) {
            cc.log(str);
            return null;
        },
        w: function (str) {
            cc.warn(str);
            return null;
        }
    },
    view: {
        //---------------------节点管理------------------------------
        //创建节点
        createNode() {
            let node = new cc.Node();
            return node;
        },
        //添加节点
        addChild(item, child, zIndex = 0) {
            if (!child) return console.log("addChild error ->请传入子节点");
            if (!item) return console.log("addChild error ->请传入父节点");
            let node = item.node ? item.node : item;
            if (!node) return console.log("addChild error ->请传入父节点");
            if (child.parent)
                return pg.logger.w("此节点已经有父节点->" + child.name);
            if (zIndex >= 0) {
                node.addChild(child, zIndex)
            } else {
                node.addChild(child);
            }
            return true;
        },
        //删除子节点
        removeChild(item, child) {
            if (!item) return pg.logger.w("节点remove失败,传入了错误的item");
            let node = item.node ? item.node : item;
            if (child.parent && child.parent == node) {
                node.removeChild(child);
                child.destroy();
            }
            return true;
        },
        removeSelf(item) {
            if (!item) return pg.logger.w("节点移除失败,传入了错误的item");
            let node = item.node ? item.node : item;
            node.removeFromParent();
            node.destroy();
        },
        //删除所有子节点
        removChildren(item) {
            if (!item) return pg.logger.w("节点remove失败,传入了错误的item");
            let node = item.node ? item.node : item;
            if (!node.parent) return pg.logger.w("节点remove失败,传入了错误的item");
            node.removeAllChildren();
            return true;
        },
        //查找节点
        find(item, childPath): cc.Node {
            if (typeof item == "string") {
                childPath = item;
                item = null;
            }
            if (!childPath || childPath == '' || typeof childPath != 'string') return pg.logger.w("findChildByPath error->" + "请传入路径");
            let child = null;
            if (item) {
                let node = item.node ? item.node : item;
                if (!node.children || node.children.length == 0) return pg.logger.w("findChild error->" + "找不到此节点，请检查层级路径:" + childPath);
                child = cc.find(childPath, node);
            } else {
                child = cc.find(childPath);
            }
            if (!child) return pg.logger.w("findChildByPath error->" + "找不到此节点，请检查层级路径:" + childPath);
            return child;
        },
        //--------------------触控事件--------------------------------
        //添加监听
        touchOn(item, callback, target) {
            if (!item) return pg.logger.w("添加按钮响应失败,传入了错误的item");
            if (!callback || !target) return pg.logger.w("添加按钮响应失败,传入了空回调");
            let node = item.node ? item.node : item;
            node.on(cc.Node.EventType.TOUCH_END, callback, target);
            return true;
        },
        //移除监听
        touchOff(item, callback, target) {
            if (!item) return pg.logger.w("移除按钮响应失败,传入了错误的item");
            if (!callback || !target) return pg.logger.w("移除按钮响应失败,传入了空回调");
            let node = item.node ? item.node : item;
            if (!node || !node.parent) return pg.logger.w("节点已移除");
            node.off(cc.Node.EventType.TOUCH_END, callback, target);
            return true;
        },
        //是否允许点击
        touchEnable(item, isEnable) {
            if (!item) return pg.logger.w("设置按钮响应失败,传入了错误的item");
            let node = item.node ? item.node : item;
            if (!node) return pg.logger.w("设置按钮响应失败,传入了错误的item");
            let btn = node.getComponent(cc.Button);
            if (!btn) return pg.logger.w("当前节点没有添加button->" + node.name);
            btn.interactable = isEnable;
            return true;
        },
        //-------------------渲染相关---------------------------------
        //显示隐藏
        visible(item, isVisible) {
            if (!item) return pg.logger.w("节点显示失败,传入了错误的item");
            let node = item.node ? item.node : item;
            if (!node || !node.parent) return pg.logger.w("节点已移除");
            node.active = isVisible;
            return true;
        },
        //更换图片
        setImg(item, res) {
            return new Promise((resolve, reject) => {
                if (!item) return pg.logger.w("图片更换失败,传入了错误的item");
                if (!res) return pg.logger.w("图片更换失败,传入了错误的res");
                pg.load.loadImg(res).then((spriteFrame: cc.SpriteFrame) => {
                    if (!cc.isValid(item)) return pg.logger.i("节点已销毁");
                    let node = item.node ? item.node : item;
                    if (!cc.isValid(node)) return pg.logger.i("节点已销毁");
                    let component = node.getComponent(cc.Sprite);
                    let { width, height } = spriteFrame.getRect();
                    component.spriteFrame = spriteFrame;
                    resolve({ width, height });
                })
            })
        },
        setNetImg(item, res, { w, h }, custom = true) {
            return new Promise((resolve, reject) => {
                if (!item) return pg.logger.w("图片更换失败,传入了错误的item");
                let node = item.node ? item.node : item;
                if (!res) {
                    if (node) node.active = false;
                    return pg.logger.w("图片更换失败,传入了错误的res");
                }
                if (!node) return pg.logger.w("图片更换失败,传入了错误的item");
                if (node.net_url == res) return;
                let nodeActive = node.active;
                node.active = false;//
                pg.load.loadNetImg(res).then((texture: cc.Texture2D) => {
                    if (!cc.isValid(node)) return pg.logger.i("节点已销毁");
                    let nw = node.width = texture.width;
                    let nh = node.height = texture.height;
                    let component = node.getComponent(cc.Sprite);
                    let spriteFrame = new cc.SpriteFrame(texture);
                    component.spriteFrame = spriteFrame;
                    node.net_url = res;
                    if (!cc.isValid(node)) return pg.logger.i("节点已销毁");
                    if (!node) return pg.logger.w("节点已销毁");
                    if (w && h && custom) {
                        let scaleX = w / nw;
                        let scaleY = h / nh;
                        let scale = scaleX <= scaleY ? scaleX : scaleY;
                        node.width = nw * scale;
                        node.height = nh * scale;
                    }
                    node.active = nodeActive;
                    resolve({ w: nw, h: nh });
                })
            })
        },
        setNodeImg(item, target) {
            return new Promise((resolve, reject) => {
                if (!item) return pg.logger.w("图片更换失败,传入了错误的item");
                let spriteFrame = target.getComponent(cc.Sprite).spriteFrame;
                let node = item.node ? item.node : item;
                if (!cc.isValid(node)) return pg.logger.i("节点已销毁");
                let component = node.getComponent(cc.Sprite);
                let { width, height } = spriteFrame.getRect();
                component.spriteFrame = spriteFrame;
                resolve({ width, height });
            })
        },
        switchMaterial(item, id0 = 0, id1 = 1) {
            let material0 = item.getMaterial(id0);
            let material1 = item.getMaterial(id1);
            item.setMaterial(id0, material1);
            item.setMaterial(id1, material0);
        },
        //更改文字
        setString(item, text, count = 0) {

            if (!item) return pg.logger.w("节点text失败,传入了错误的item");
            if (count > 0) {
                let parse_str = function (str, count) {
                    if (!str) return "";
                    var segmants = str.split('+');
                    str = segmants.join('');
                    var len = 0;
                    var idx = 0;
                    for (var i = 0; i < str.length; i++) {
                        var p = /[^x00-xff]/g;
                        var a = str.charAt(i);
                        if (p.test(a)) {
                            len += 2;
                            idx++;
                        }
                        else {
                            len += 1;
                            idx++;
                        }
                        if (len >= count * 2)
                            break;
                    }
                    return str.substr(0, idx);
                }
                text = parse_str(text, count);
            }
            let node = item.node ? item.node : item;
            if (!node) return;
            let component = node.getComponent(cc.Label);//组件功能 非node的功能
            component.string = text;
            return true;
        },
        //更变颜色
        setColor(item, color, outlineWidth = -1) {
            if (!item) return pg.logger.w("setColor warn->传入了错误的item");
            let RGB = this.colorRgb(color);
            if (!RGB || RGB.length == 0) return pg.logger.w("color ->传入了错误的color");
            item.color = new cc.Color(RGB[0], RGB[1], RGB[2]);
            if (outlineWidth < 0) return;
            let LabelOutline = item.getComponent(cc.LabelOutline);
            if (!LabelOutline) return pg.logger.w("LabelOutline warn->未添加描边");
            LabelOutline.width = outlineWidth;
        },
        colorRgb(color) {
            var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
            var sColor = color;
            if (sColor && reg.test(sColor)) {
                if (sColor.length === 4) {
                    var sColorNew = "#";
                    for (var i = 1; i < 4; i += 1) {
                        sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1));
                    }
                    sColor = sColorNew;
                }
                //处理六位的颜色值
                var sColorChange = [];
                for (var i = 1; i < 7; i += 2) {
                    sColorChange.push(parseInt("0x" + sColor.slice(i, i + 2)));
                }
                return sColorChange;
            } else {
                return sColor;
            }
        },
        //-------------------动画播放--------------------------------
        playSpineAnimation(item, aniName, loop) {
            if (!item || !cc.isValid(item)) return pg.logger.w("动画播放失败,传入了错误的item");
            if (!aniName) return pg.logger.w("动画播放失败,传入了错误的aniName");
            let node = item.node ? item.node : item;
            if (!cc.isValid(node)) return pg.logger.w("节点已销毁");
            let skl = node.getComponent(sp.Skeleton);
            skl.setAnimation(0, aniName, loop);
            return skl;
        },
        addEventSpineAnimation(item, callback, target) {
            if (!item || !cc.isValid(item)) return pg.logger.w("动画播放失败,传入了错误的item");
            let node = item.node ? item.node : item;
            if (!cc.isValid(node)) return pg.logger.w("节点已销毁");
            let skl: sp.Skeleton = node.getComponent(sp.Skeleton);
            if (!skl) return pg.logger.w("无动画实体");
            // 添加循环完成的监听器
            skl.setCompleteListener(function (trackEntry, loopCount) {
                // 在此处编写循环完成时要执行的代码
                callback.call(target);
            });
            return skl;
        },
        playDBAnimation(item, aniName, loop = -1) {
            if (!item || !cc.isValid(item)) return pg.logger.w("动画播放失败,传入了错误的item");
            if (!aniName) return pg.logger.w("动画播放失败,传入了错误的aniName");
            let node = item.node ? item.node : item;
            if (!cc.isValid(node)) return pg.logger.w("节点已销毁");
            let skl: dragonBones.ArmatureDisplay = node.getComponent(dragonBones.ArmatureDisplay);
            if (!skl) return pg.logger.w("无动画实体");
            skl.playAnimation(aniName, loop);
            return skl;
        },
        addEventDBAnimation(item, callback, target) {
            if (!item || !cc.isValid(item)) return pg.logger.w("动画播放失败,传入了错误的item");
            let node = item.node ? item.node : item;
            if (!cc.isValid(node)) return pg.logger.w("节点已销毁");
            let skl: dragonBones.ArmatureDisplay = node.getComponent(dragonBones.ArmatureDisplay);
            if (!skl) return pg.logger.w("无动画实体");
            skl.on(dragonBones.EventObject.LOOP_COMPLETE, callback, target)
            return skl;
        },
        clone(node: cc.Node) {
            return cc.instantiate(node)
        }
    },
    load: {
        //资源加载
        // loadBundle: function (bundleName) {
        //     return new Promise((resolve, reject) => {
        //         // let hallBundle = cc.assetManager.getBundle(`hall`);
        //         // hallBundle.releaseAll();
        //         // cc.assetManager.removeBundle(hallBundle);
        //         let bundle = cc.assetManager.getBundle(bundleName);
        //         if (bundle) return resolve('');
        //         cc.assetManager.loadBundle(bundleName, (err, bundle) => {
        //             if (err) return cc.error(err);
        //             resolve(bundle);
        //         });
        //     });
        // },
        loadRes: function (res, type, bundleName) {
            //此处需要二次封装，新的存在assetbundle
            return new Promise((resolve, reject) => {
                cc.loader.loadRes(res, type, (err, data) => {
                    if (err && !data) return resolve(pg.logger.d('loading loadRes error-> ' + res));
                    resolve(data);
                });
            })
        },
        loadImg: function (url) {
            return new Promise((resolve, reject) => {
                this.loadRes(url, cc.SpriteFrame).then((data) => {
                    if (!data || data.length == 0) return;
                    resolve(data);
                })
            })
        },
        loadPrefab: function (path = "") {
            return new Promise((resolve, reject) => {
                let url = "/prefabs/" + path;
                this.loadRes(url, cc.Prefab).then((data) => {
                    if (!data || data.length == 0) return reject();
                    return resolve(cc.instantiate(data));
                })
            })
        },
        loadNetImg: function (url) {
            return new Promise((resolve, reject) => {
                cc.loader.load({ url }, (err, texture) => {
                    if (err && !texture) return resolve(pg.logger.w('loading loadRes warn-> ' + texture));
                    resolve(texture);
                });
            })
        },
        loadDB(item, config) {
            return new Promise((resolve, reject) => {
                if (!config.png || !config.ske || !config.tex) {
                    return resolve('');
                }
                pg.load.loadNetRes(config.png).then((png: any) => {
                    pg.load.loadNetRes(config.ske).then((ske: any) => {
                        pg.load.loadNetRes(config.tex).then((tex: any) => {
                            var atlas = new dragonBones.DragonBonesAtlasAsset();
                            atlas.atlasJson = JSON.stringify(tex.json);
                            atlas.texture = png;

                            var asset = new dragonBones.DragonBonesAsset();
                            asset.dragonBonesJson = JSON.stringify(ske.json);

                            let dragonDisplay = item.getComponent(dragonBones.ArmatureDisplay);

                            dragonDisplay.dragonAtlasAsset = atlas;
                            dragonDisplay.dragonAsset = asset;

                            //设置
                            let defaultArmature = ske.json.armature[0];//aramture  所有的骨骼名称
                            dragonDisplay.armatureName = defaultArmature.name;
                            resolve(defaultArmature.animation[0].name);
                        })
                    })
                })

            });
        },
        loadNetRes: function (url) {
            return new Promise((resolve, reject) => {
                cc.assetManager.loadRemote(url, (err, info) => {
                    if (err) return resolve(pg.logger.w('loading loadRes warn-> ' + err));
                    resolve(info);
                });
            })
        },
    },
    localStorage: {
        //本地存储
        setItem: function (key, val) {
            cc.sys.localStorage.setItem(key, val);
        },
        getItem: function (key, defVal) {
            return cc.sys.localStorage.getItem(key) || defVal;
        }
    },
    http: {
        //http访问
        send: function (type, url, data, callback) {
            return new Promise((resolve) => {
                return setTimeout(() => {
                    return resolve({ status: 200 });
                }, 100 + Math.random() * 300);
                let xhr = cc.loader.getXMLHttpRequest();
                xhr.timeout = 5000;
                xhr.responseType = "text";
                xhr.open(type, url, true);
                xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
                xhr.onreadystatechange = () => {
                    if (xhr.readyState !== 4) return;
                    if (xhr.status >= 200 && xhr.status < 300) {
                        try {
                            let resp = xhr.responseText;
                            pg.logger.d("resp->" + JSON.stringify(resp));
                            callback(resp);
                            resolve(resp);
                        } catch (e) {
                        }
                    }
                    else {

                    }
                };
                xhr.onerror = (e) => {
                    pg.logger.w("onerror->" + url);
                };
                xhr.ontimeout = (e) => {
                    pg.logger.w("ontimeout->" + url);
                };
                xhr.send(data);
            })

        }
    },
    audio: {
        playAudioByUrl(audio_url, cb = null, loadCb = null) {
            return new Promise((resolve, reject) => {
                if (!audio_url) return resolve(null);

                cc.assetManager.loadRemote(audio_url, (err, audioClip: any) => {
                    cc.audioEngine.stopAll();
                    const audioId = cc.audioEngine.play(audioClip, false, 0.8);
                    cc.audioEngine.setFinishCallback(audioId, () => {
                        cb && cb();
                        resolve(audioId);
                    });
                    loadCb && loadCb(audioId);
                });
            });
        },
        playBgAudioByUrl(audio_url, cb = null, loadCb = null) {
            return new Promise((resolve, reject) => {
                if (!audio_url) return resolve(null);
                cc.assetManager.loadRemote(audio_url, (err, audioClip: any) => {
                    const audioId = cc.audioEngine.playMusic(audioClip, true);
                    cc.audioEngine.setFinishCallback(audioId, () => {
                        cb && cb();
                    });
                    loadCb && loadCb(audioId);
                    resolve(audioId);
                });
            });
        },
        playLocalBgm(audio: cc.Node, volume = 0.7) {
            return new Promise((resolve, reject) => {
                const id = cc.audioEngine.playMusic(audio.getComponent(cc.AudioSource).clip, true);
                cc.audioEngine.setVolume(id, volume);
                cc.audioEngine.setFinishCallback(id, () => {
                    resolve(id);
                });
            })
        },
        stopLocalBgm() {
            cc.audioEngine.stopMusic();
        },
        stopAudio(audioId) {
            if (!audioId) return;
            cc.audioEngine.stopEffect(audioId);
        },
        playAudio(audioClip, cb = null) {
            if (audioClip) {
                const audioId = cc.audioEngine.playEffect(audioClip, false);
                if (cb) {
                    cc.audioEngine.setFinishCallback(audioId, () => {
                        cb && cb();
                    });
                }
                return audioId;
            }
        },
        playLocalAudio(audio: cc.Node, volume = 1) {
            return new Promise((resolve, reject) => {
                const id = cc.audioEngine.playEffect(audio.getComponent(cc.AudioSource).clip, false);
                cc.audioEngine.setVolume(id, volume);
                cc.audioEngine.setFinishCallback(id, () => {
                    resolve(id);
                });
            })
        },
        getAudioClip(audio_url) {
            return new Promise((resolve, reject) => {
                if (!audio_url) return resolve(null);
                cc.assetManager.loadRemote(audio_url, (err, audioClip: any) => {
                    resolve(audioClip);
                });
            });
        }

    },
    time: {
        delay(time) {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve('');
                }, time * 1000);
            });
        },
        delayCtrl(): TimeCtrl {
            return new TimeCtrl();
        }
    },
    hw: {
        playLocalAudio(audioName, loop = false) {
            console.log("play audio->" + audioName);
            const audio = cc.find(`Canvas/res/audio/${audioName}`).getComponent(cc.AudioSource);
            return new Promise((resolve, reject) => {
                const id = cc.audioEngine.playEffect(audio.clip, loop);
                resolve(id);
            })
        }
    },
    signal: {
        createSignal(): Signal {
            let event = new Signal<any>();
            return event;
        }
    },
    event: Emitter.getInstance()
};
export default pg;
