
interface ResData {
    /**
     * 分组数据
     */
    groups: GroupInt[];
    //暂时没有工具，不用
    resources?: any;
    path?: string;
}

interface GroupInt {
    /**
     * 所有的资源名字，根据,分割，根据后缀区分类型
     * 
     */
    keys: string;//"aa.png,bb.jpg,name.json"
    /**
     * 文件夹名字吧
     */
    name: string;
    /**
     * 图集
     * 线上打包合图可能有多张，暂时发现texturePacker版本问题只有一张
     */
    atlas: {
        [name: string]: {
            "x": number,
            "y": number,
            "w": number,
            "h": number,
            "ox": number,
            "oy": number,
            "sw": number,
            "sh": number,
            "ro": boolean
        },
    };
}

interface SkinInt {
    name: string,
    x: 0,
    y: 0,
    type: 'container' | 'text' | 'button' | 'sprite' | 'rect' | 'item',//item的不初始化进节点，只作为数据
    children?: SkinInt[],
    id?: string,
    alpha?: number,
    rotation?: number,
    props?: {
        //图片用到
        source?: string,

        //文本用到
        text: string,
        size: number,
        fillColor: string,
        textAlpha: number,
        textWidth: number,//如果不存在应该无该属性
        textHeight: number,//如果不存在应该无该属性
        textAlign: FYGE.TEXT_ALIGN,//如果不存在应该无该属性
        verticalAlign: FYGE.VERTICAL_ALIGN,////如果不存在应该无该属性
        lineHeight: number,//如果不存在应该无该属性

        //矩形，或图片可能用到
        width: number,
        height: number,

        //按钮用到
        tUp: string,
        tDown: string,
        tDisable: string
    }
}

/**
 * 简单点，有工具的话像egret那样玩，可以自动生成图片组数据
 */
export namespace RES {
    let resData: ResData
    /**
     * 资源路径
     */
    export let resPath: string;
    /**
     * RES单独缓存一下纹理，全局的FYGE缓存纹理对于多page有覆盖的问题；
     */
    let textureHash: {
        [name: string]: FYGE.Texture;
    } = {};
    /**
     * movieClip的ve数据
     */
    let videoEntityHash: {
        [name: string]: SvgaParser.VideoEntity
    } = {};
    /**
     * spine的骨骼动画数据
     */
    let spineDataHash: {
        [name: string]: FYGE.ISkeletonData
    } = {};
    //lottie的先不考虑加入，因为用它是为了加载以及淘宝上的问题
    /**
     * 记录组加载完成
     */
    let groupsCompleteHash: {
        [name: string]: boolean
    } = {}
    /**
     * 记录加载的promise
     */
    let groupsPromiseHash: {
        [name: string]: Promise<any>
    } = {}
    /**
     * 单独资源加载的promise记录
     */
    let singleResPromiseHash: {
        [name: string]: Promise<any>
    } = {}

    /**
     * 
     * @param res 资源数据，就是对象，不考虑加载json先
     * res格式{
     * path:1111/
     * groups: [
     * {
     * 
     * }
     * ];
     * }
     * @param path 
     */
    export function loadConfig(res) {
        resData = res;
        resPath = FYGE.getEnv() == "tb" ? res.path : window["resPath"] || res.path;
    }
    /**
     * 根据组名加载一组资源，通常用于加载一个视图的的所有资源
     * 里的promise的resolve并没有返回值
     * @param name 
     */
    export function loadGroup(name: string): Promise<void> {
        //已经加载完成的直接返回
        if (groupsCompleteHash[name]) {//其实直接return就行
            // return new Promise((resolve) => {
            //     resolve()
            // })
            return Promise.resolve();
        }
        //如果是正在加载中的，返回正在加载中的promise
        if (groupsPromiseHash[name]) {
            return groupsPromiseHash[name];
        }
        //如果首次加载
        //获取资源组
        let arr = getGroupResByName(name);
        //如果不存在arr，直接返回空p，且标记完成
        if (!arr || !arr.length) {
            groupsCompleteHash[name] = true;
            // return new Promise((resolve) => {
            //     resolve()
            // })
            return Promise.resolve()
        }
        // 建一个promise
        let p: Promise<void> = new Promise((resolve, reject) => {
            loadResList((s) => {
                //移除
                delete groupsPromiseHash[name];
                if (s) {
                    groupsCompleteHash[name] = true;
                    resolve()
                } else {
                    reject();
                }
            }, arr/*, resPath + name*/)
        })
        groupsPromiseHash[name] = p;
        return p;
    }
    /**
     * var textue = await RES.getResAsync(str);
     * @param str 可以是网络图片路径或键值
     * @param comFun 加载回调
     * @param thisObj this指向
     */
    export function getResAsync(str: string, comFun?: (res: any, str: string) => void, thisObj?: any): Promise<any> {
        // var arr = str.split(".");
        var type = str.substring(str.lastIndexOf(".") + 1, str.length);
        //如果是图片
        if (type == "png" || type == "jpg") {
            //原先就有了，加载过的，且已加载完成的
            let cached = textureHash[str] || FYGE.TextureCache[str];
            if (cached) {
                //回调形式
                comFun && comFun.call(thisObj, cached, str)
                // return cached;
                // return new Promise((r) => { //为了能.then
                //     r(cached)
                // })
                return Promise.resolve(cached)
            }
            //未加载完成的
            else if (singleResPromiseHash[str]) {
                return returnSingleResPromise(str, comFun, thisObj)
            }
            else {
                //判断是否在资源里，判断是否要加载图集，注意已排除jpg
                var groupName = hasRes(str);
                if (groupName && type != "jpg") {
                    var group = getGroupByName(groupName);
                    if (group && group.atlas) {
                        //加载图集，现在就一张，以后有机会改
                        var sht = groupName + ".sht";//group.atlas.split(",")[0];
                        //找sht是否在加载中，没有就走加载的promise
                        return (singleResPromiseHash[sht] || getResAsync(sht)).then(
                            (r) => {
                                //正在加载中，getResAsync首次加载的回调会缓存，完成后返回需要的，
                                let cached = textureHash[str] || FYGE.TextureCache[str]
                                comFun && comFun.call(thisObj, cached, str)
                                return cached;
                            },
                            () => {
                                comFun && comFun.call(thisObj, null, str)
                                return null
                            }
                        )
                    }
                }
                var src = groupName ? resPath + groupName + "/" + str : str;
                var p = new Promise((resolve, reject) => {
                    FYGE.GlobalLoader.loadImage((s, image) => {
                        //移除
                        delete singleResPromiseHash[str];
                        //入缓存
                        if (s) {
                            let cached = FYGE.Texture.from(image);
                            //入RES,
                            textureHash[str] = cached;
                            //入全局
                            FYGE.Texture.addToCache(cached, str);
                            comFun && comFun.call(thisObj, cached, str)
                            resolve(cached)
                        } else {
                            comFun && comFun.call(thisObj, null, str)
                            reject()
                        }
                    }, src)
                })
                singleResPromiseHash[str] = p
                return p
            }
        }
        //淘宝小程序时不用，也千万别把svga放入其他组，开发时和线上做区分，单独加载，用svgaTb
        else if (type == "svga") {//暂时不用，
            if (videoEntityHash[str]) {
                comFun && comFun.call(thisObj, videoEntityHash[str], str)
                return Promise.resolve(videoEntityHash[str])
            }
            //未加载完成的
            else if (singleResPromiseHash[str]) {
                return returnSingleResPromise(str, comFun, thisObj)
            } else {
                var groupName = hasRes(str);
                var src = groupName ? resPath + groupName + "/" + str : str;
                var p = new Promise((resolve, reject) => {
                    SvgaParser.loadSvga(
                        src,
                        (v) => {
                            delete singleResPromiseHash[str];
                            videoEntityHash[str] = v;
                            comFun && comFun.call(thisObj, v, str)
                            resolve(v)
                        },
                        (err) => {
                            delete singleResPromiseHash[str];
                            comFun && comFun.call(thisObj, null, str)
                            reject(err)
                        }
                    )
                })
                singleResPromiseHash[str] = p;
                return p
            }
        }
        //sht图集的话，不晓得用啥判断加载完成，所以不删除promise吧，
        else if (type == "sht") {//而且现在图集数据都是在atlas代码里，以后如果考虑cdn加载的话，注意修改文件后缀
            if (singleResPromiseHash[str]) {
                return returnSingleResPromise(str, comFun, thisObj)
            } else {
                var groupName = hasRes(str);//数据现在肯定在内了
                var src = groupName ? resPath + groupName + "/" + str : str;
                var p = new Promise((resolve, reject) => {
                    //现在在代码内，直接取，所以只加载图片，否则用loadSheet传src
                    var jsonData = getGroupByName(groupName).atlas;
                    jsonData = typeof jsonData == "object" ? jsonData : null;//转下，反正现在字符串的atlas没用
                    (jsonData ? FYGE.GlobalLoader.loadImage : FYGE.GlobalLoader.loadSheet.bind(FYGE.GlobalLoader))((s, data) => {
                        if (s) {
                            //createTextureSheet会自行缓存全局
                            var t = FYGE.createTextureSheet(new FYGE.BaseTexture(jsonData ? data : data.img), jsonData || data.json)
                            //缓存进RES
                            for (let key in t) textureHash[key] = t[key];
                            comFun && comFun.call(thisObj, t, str)
                            resolve(t)
                        } else {
                            //加载失败时，需要移除，否则再次触发加载会出问题
                            delete singleResPromiseHash[str];
                            comFun && comFun.call(thisObj, null, str)
                            reject()
                        }
                    }, jsonData ? src.slice(0, -3) + "png" : src)
                })
                singleResPromiseHash[str] = p
                return p
            }
        }
        //spineData加载
        else if (type == "spi") {
            if (spineDataHash[str]) {
                comFun && comFun.call(thisObj, spineDataHash[str], str)
                return Promise.resolve(videoEntityHash[str])
            }
            //未加载完成的
            else if (singleResPromiseHash[str]) {
                return returnSingleResPromise(str, comFun, thisObj)
            } else {
                var groupName = hasRes(str);
                var src = groupName ? resPath + groupName + "/" + str : str;
                var p = new Promise((resolve, reject) => {
                    FYGE.loadSpine(
                        src,
                        (s) => {
                            delete singleResPromiseHash[str];
                            spineDataHash[str] = s;
                            comFun && comFun.call(thisObj, s, str)
                            resolve(s)
                        },
                        (err) => {
                            delete singleResPromiseHash[str];
                            comFun && comFun.call(thisObj, null, str)
                            reject(err)
                        }
                    )
                })
                singleResPromiseHash[str] = p;
                return p
            }
        }
    }

    /**
     * 待写，根据网络路径加载图片
     */
    export function getResByUrl() {

    }

    /**
     * 获取素材，
     * @param str 
     * @return 已加载好得素材或null
     */
    export function getRes(str: string)/*: Texture | VideoEntity*/ {
        if (!str) return null;
        var type = str.substring(str.lastIndexOf(".") + 1, str.length);
        if (type == "png" || type == "jpg") {
            return textureHash[str] || FYGE.TextureCache[str];
        }
        else if (type == "svga") {
            return videoEntityHash[str];
        }
        else if (type == "spi") {
            return spineDataHash[str];
        }
        return null
    }
    /**
     * 偷懒的方法，加载配置里所有的资源，基本也不用
     */
    export function loadAllGroup() {
        var groups = resData.groups;
        var p = []
        groups.forEach((g) => {
            p.push(loadGroup(g.name))
        })
        return Promise.all(p)
    }

    /**
     * 判断是否在资源组里
     * 考虑是否init就做表
     * 有就返回组名，为了加载路径，不然以后有工具可以放入resources
     */
    function hasRes(str: string): string {
        for (var i = 0; i < resData.groups.length; i++) {
            var group = resData.groups[i];
            var keys = group.keys;
            if (keys && keys.split(",").indexOf(str) > -1) {
                return group.name;
            }
            //如果是图集的json,altas现在是图集
            if (group.atlas && group.name + ".sht" == str) {
                return group.name;
            }
        }
        return null
    }

    /**
     * 处理数据，获得所有资源单项
     * @param name 
     */
    function getGroupResByName(name: string) {
        var group: GroupInt = getGroupByName(name);
        if (!group) return null;
        //判断加载图集还是单图
        if (group.atlas) {
            // var arr: string[] = [].concat(group.atlas.split(","));
            var arr = [name + ".sht"]
            //再添加非图片的资源，和图集已排除jpg
            if (group.keys) {
                arr = arr.concat(group.keys.split(",").filter((k: string) => {
                    return k.substr(-4) != ".png" //&& k.substr(-4) != ".jpg"
                }))
            }
            return arr
        }
        else if (group.keys) {
            return group.keys.split(",")
        } else {
            return null
        }
    }

    /**
     * 根据名字找组
     * @param name 
     */
    function getGroupByName(name: string): GroupInt {
        var groups = resData.groups;
        var group: GroupInt;
        for (var i = 0; i < groups.length; i++) {
            if (groups[i].name === name) {
                group = groups[i];
                break;
            }
        }
        return group
    }

    /**
     * 新版的加载一列资源
     * @param callback 
     * @param arr 
     */
    function loadResList(callback: (allLoaded: boolean) => void, arr: string[]) {
        let count = 0;
        let countAll = arr.length;
        if (!countAll) callback(true);
        let mark = true;
        for (var i = 0; i < countAll; i++) {
            let resName = arr[i];
            getResAsync(resName, (res, str) => {
                //标记失败，如果有一项资源加载失败，标记下
                if (!res) mark = false
                if (++count == countAll) callback(mark);
            }, this)
        }
    }

    /**
     * 
     * @param str 
     * @param comFun 
     * @param thisObj 
     */
    function returnSingleResPromise(str: string, comFun?: (res: any, str: string) => void, thisObj?: any) {
        //已判断是否存在
        singleResPromiseHash[str].then(
            (r) => {
                comFun && comFun.call(thisObj, r, str)
            },
            () => {
                comFun && comFun.call(thisObj, null, str)
            }
        )
        return singleResPromiseHash[str];
    }

    //皮肤相关的也放在RES吧
    let skinData: SkinInt
    /**
     * 添加皮肤配置文件
     */
    export function loadSkinConfig(skinJson) {
        skinData = skinJson;
    }
    /**
     * 根据
     * @param con 添加显示对象的容器
     * @param skin 皮肤名字或数据
     * @param root 根容器，为了添加自定义引用
     */
    export function initSkinDisplay(con: FYGE.Container, skin: string | SkinInt, root?: FYGE.Container) {
        //@ts-ignore
        var data: SkinInt = typeof (skin) == 'string' ? getSkinDataByName(skin) : skin;
        if (!data.children || !data.children.length) return;
        // for (var i = data.children.length - 1; i >= 0; i--) {
        for (var i = 0; i < data.children.length; i++) {
            var child = data.children[i];
            if (child.type == "item") continue;
            var dis = con.addChild(getDisplayByData(child));
            if (root && child.id) root[child.id] = dis;
            if (child.type == "container") initSkinDisplay(dis, child, root);
        }
    }
    /**
     * 遍历根据名字找节点数据，只会是container的
     * @param skinName 
     */
    export function getSkinDataByName(skinName: string, skinNode: SkinInt = skinData): SkinInt {
        if (!skinNode || !skinNode.children || !skinNode.children.length) return null;
        for (var i = 0; i < skinNode.children.length; i++) {
            var child = skinNode.children[i];
            if (child.name == skinName && (child.type == "container" || child.type == "item")) return child;
            var gson = getSkinDataByName(skinName, child);
            if (gson) return gson
        }
        return null;
    }


    /**
     * 通过数据创建显示对象
     * @param data 
     */
    function getDisplayByData(data: SkinInt): FYGE.Container {
        var dis: FYGE.Container;
        switch (data.type) {
            case "container":
                dis = new FYGE.Container();
                break;
            case "button":
                dis = new FYGE.Button(
                    getRes(data.props.tUp),
                    data.props.tDown ? getRes(data.props.tDown) : null,
                    data.props.tDisable ? getRes(data.props.tDisable) : null,
                );
                break;
            case "text":
                dis = new FYGE.TextField();
                for (let key in data.props) dis[key] = data.props[key];
                break;
            case "sprite":
                dis = new FYGE.Sprite(getRes(data.props.source));
                //如果有宽高
                if (data.props.width) dis.width = data.props.width;
                if (data.props.height) dis.height = data.props.height;
                break;
            case "rect":
                // dis = new FYGE.Graphics()
                //     .beginFill(data.props.fillColor)
                //     .drawRect(0, 0, data.props.width, data.props.height)
                //     .endFill();
                dis = new FYGE.Shape()
                //@ts-ignore
                dis.beginFill(FYGE.string2hex(data.props.fillColor))
                //@ts-ignore
                dis.drawRect(0, 0, data.props.width, data.props.height)
                //@ts-ignore
                dis.endFill();
                break;
        }
        dis.name = data.name;
        dis.alpha = typeof data.alpha == "number" ? data.alpha : 1;
        dis.rotation = data.rotation || 0;
        dis.position.set(data.x, data.y);
        // if (data.type == "text") dis.y -= 4;//文本莫名偏下，移动下，手机调试的时候也试试
        return dis;
    }

    /**
     * 销毁组纹理
     * 线上才有用，待测试，TODO
     * @param name 
     */
    export function destroyGroup(name: string) {
        var group: GroupInt = getGroupByName(name);
        if (!group) return;
        //删除缓存吧，执行loadGroup还能继续加载
        delete groupsCompleteHash[name];
        //同时也删除掉对应图集的singleResPromiseHash，否则不会继续走加载
        delete singleResPromiseHash[name + ".sht"];
        var arr = [];
        if (group.keys) {
            arr = group.keys.split(",")
        }
        var removedBase = [];
        //散图清除
        for (var i = 0; i < arr.length; i++) {
            var t: FYGE.Texture = getRes(arr[i]);
            //不是纹理的不处理
            if (!t || !t.baseTexture) continue;
            // if (t) {
            //base的清除，不要重复清除
            if (removedBase.indexOf(t.baseTexture) == -1) {
                t.baseTexture.destroy();
                removedBase.push(t.baseTexture)
            }
            //自己纹理清除
            t.destroy();
            // }
            //RES里单独缓存的清除
            delete textureHash[arr[i]]
        }
    }

    //貌似不需要，为了加载过一次的资源不用重新加载
    function destroyRES() {

    }
}


