import Logger from "./Logger.js"

const logger = new Logger('XAvatarLoader')

export default class XAvatarLoader {
    constructor() {
        this.containers = new Map,
        this.meshes = new Map,
        this.animations = new Map,
        this.aniPath = new Map,
        this.binPath = new Map,
        this.texPath = new Map,
        this.matPath = new Map,
        this.mshPath = new Map,
        this.rootPath = new Map,
        this.meshTexList = new Map,
        this._enableIdb = !0,
        this._mappings = new Map,
        this._sharedTex = new Map,
        this.avaliableAnimation = new Map,
        this.enableShareTexture = !0,
        this.enableShareAnimation = !0,
        this.fillEmptyLod = !0,
        this.pendantMap = new Map;
        const e = new BABYLON.GLTFFileLoader;
        BABYLON.SceneLoader.RegisterPlugin(e),
        e.preprocessUrlAsync = function(i) {
            const o = avatarLoader._mappings.get(i);
            return o ? Promise.resolve(o) : Promise.resolve(i)
        }
    }
    _parsePendant(e, i) {
        if (!e || !i) {
            logger.error("[Engine] invalid id or url when loading pendant");
            return
        }
        const o = ".zip"
          , s = i.replace(o, "/");
        this.pendantMap.set(e, s)
    }
    pullAndLoadXObject(e, i) {
        const o = avatarLoader.pendantMap.get(i);
        return Tools.LoadFileAsync(o + `${i}.json`, !1).then(s=>{
            if (!(s instanceof ArrayBuffer))
                return LoadXObject(o, s).then(c=>c)
        }
        )
    }
    getParsedUrl(e, i, o, s="") {
        return new Promise((c,d)=>{
            if (!o || o.indexOf(".zip") === -1)
                return c(o);
            const _ = this.rootPath.get(o);
            if (_)
                return c(_);
            {
                const b = ".zip"
                  , k = o.replace(b, "") + COMPONENT_LIST_PREFIX;
                e.urlTransformer(k, !0).then(j=>{
                    if (!j)
                        return d("Loading Failed");
                    new Response(j).json().then($=>{
                        var tt, rt, it, nt, ot, at, st;
                        const _e = o.replace(b, "")
                          , et = _e + ((tt = $ == null ? void 0 : $.components) == null ? void 0 : tt.url.replace("./", ""));
                        if (this.rootPath.set(o, et),
                        $.components ? ($.components.url && this.mshPath.set(i, _e + "/" + ((rt = $ == null ? void 0 : $.components) == null ? void 0 : rt.url.replace("./", ""))),
                        $.components.url_lod2 && this.mshPath.set(i + "_" + avatarSetting.lod[1].level, _e + "/" + ((it = $ == null ? void 0 : $.components) == null ? void 0 : it.url_lod2.replace("./", ""))),
                        $.components.url_lod4 && this.mshPath.set(i + "_" + avatarSetting.lod[2].level, _e + "/" + ((nt = $ == null ? void 0 : $.components) == null ? void 0 : nt.url_lod4.replace("./", "")))) : ($.meshes.url && this.mshPath.set(i, _e + "/" + ((ot = $ == null ? void 0 : $.meshes) == null ? void 0 : ot.url.replace("./", ""))),
                        $.meshes.url_lod2 && this.mshPath.set(i + "_" + avatarSetting.lod[1].level, _e + "/" + ((at = $ == null ? void 0 : $.meshes) == null ? void 0 : at.url_lod2.replace("./", ""))),
                        $.meshes.url_lod4 && this.mshPath.set(i + "_" + avatarSetting.lod[2].level, _e + "/" + ((st = $ == null ? void 0 : $.meshes) == null ? void 0 : st.url_lod4.replace("./", "")))),
                        $.materials && $.materials.forEach(ut=>{
                            const ct = _e + "/" + ut.url;
                            this.matPath.set(ut.name, ct)
                        }
                        ),
                        $.bin) {
                            const ut = _e + "/" + $.bin.url;
                            this.binPath.set(i, ut);
                            const ct = _e + "/" + $.bin.url_lod2;
                            this.binPath.set(i + "_" + avatarSetting.lod[1].level, ct);
                            const lt = _e + "/" + $.bin.url_lod4;
                            this.binPath.set(i + "_" + avatarSetting.lod[2].level, lt)
                        }
                        return $.textures && $.textures.forEach(ut=>{
                            const ct = _e + "/" + ut.url;
                            this.texPath.set(ut.url, ct);
                            const lt = this.meshTexList.get($.components.url);
                            ut.type === "png" && (lt ? lt.find(ft=>ft === ut.name) || lt.push(ut.url) : this.meshTexList.set(i, [ut.name]))
                        }
                        ),
                        c(et)
                    }
                    ).catch($=>{
                        d(`[Engine] parse json file error,${$}`)
                    }
                    )
                }
                ).catch(j=>{
                    d(`[Engine] ulrtransform error, cannot find resource in db,${j}`)
                }
                )
            }
        }
        )
    }
    async parse(e, i) {
        const o = [];
        i.forEach(s=>{
            this._setAnimationList(s.id, s.animations),
            o.push(this.getParsedUrl(e, s.id, s.url)),
            s.components.forEach(c=>{
                c.name === "pendant" ? c.units.forEach(d=>{
                    this._parsePendant(d.id, d.url)
                }
                ) : c.units.forEach(d=>{
                    o.push(this.getParsedUrl(e, d.name, d.url))
                }
                )
            }
            )
        }
        ),
        await Promise.all(o)
    }
    _setAnimationList(e, i) {
        i ? i.forEach(o=>{
            this.aniPath.set(e + "_" + o.name, o.url)
        }
        ) : logger.error("[Engine] no animation list exist, please check config for details")
    }
    disposeContainer() {
        const e = [];
        this.containers.forEach((i,o)=>{
            if (i.xReferenceCount < 1) {
                if (this.enableShareTexture && i.textures.length > 0) {
                    for (let s = 0; s < i.textures.length; ++s)
                        i.textures[s].xReferenceCount != null ? i.textures[s].xReferenceCount-- : i.textures[s].xReferenceCount = 0,
                        i.textures[s]._parentContainer = null;
                    i.textures = []
                }
                e.push(o)
            }
        }
        ),
        e.forEach(i=>{
            var o, s;
            (o = this.containers.get(i)) == null || o.removeAllFromScene(),
            (s = this.containers.get(i)) == null || s.dispose(),
            this.containers.delete(i)
        }
        ),
        this._sharedTex.forEach((i,o)=>{
            i.xReferenceCount == 0 && (i.dispose(),
            this._sharedTex.delete(o))
        }
        )
    }
    set enableIdb(e) {
        this._enableIdb = e
    }
    getGlbPath(e) {
        return this.aniPath.get(e + ".glb")
    }
    getGltfPath(e) {
        return this.mshPath.get(e + ".gltf")
    }
    getPngUrl(e) {
        return this.texPath.get(e + ".png")
    }
    getMeshUrl(e) {
        return this.mshPath.get(e)
    }
    _getSourceKey(e, i) {
        return i && avatarSetting.lod[i] ? e + avatarSetting.lod[i].fileName.split(".")[0] : e
    }
    _getAnimPath(e, i) {
        let o = this.aniPath.get(i + "_animations_" + i.split("_")[1]);
        return o || (o = this.aniPath.get(i + "_" + e)),
        o
    }
    load(e, i, o, s) {
        return this.loadGlb(e, i, o).then(c=>c || Promise.reject("[Engine] container load failed")).catch(()=>Promise.reject("[Engine] container load failed"))
    }
    _searchAnimation(e, i) {
        let o;
        return this.containers.forEach((s,c)=>{
            const d = i.split("_")[0];
            c.indexOf(d) != -1 && c.indexOf(e) != -1 && (o = s)
        }
        ),
        o
    }
    loadAnimRes(e, i, o) {
        const s = this._getAnimPath(i, o)
          , c = getAnimationKey(i, o);
        return s && this.containers.get(s) ? Promise.resolve(this.containers.get(s)) : s ? this._loadGlbFromBlob(e, c, s).then(d=>d.animationGroups.length == 0 ? (this.containers.delete(c),
        d.dispose(),
        Promise.reject("container does not contains animation data")) : d) : Promise.reject("no such url")
    }
    loadGlb(e, i, o) {
        let s = this.getMeshUrl(this._getSourceKey(i, o));
        return !s && this.fillEmptyLod && (o = 0,
        s = this.getMeshUrl(this._getSourceKey(i, o))),
        s && this.containers.get(s) ? Promise.resolve(this.containers.get(s)) : s ? this._enableIdb ? this._loadGlbFromBlob(e, this._getSourceKey(i, o), s) : this._loadGlbFromUrl(e, this._getSourceKey(i, o), s) : Promise.reject("no such url")
    }
    loadGltf(e, i, o, s) {
        const c = this._getSourceKey(i, o || 0);
        let d = this.getGltfPath(c);
        return !d && this.fillEmptyLod && (d = this.getGltfPath(i)),
        d && this.containers.get(d) ? Promise.resolve(this.containers.get(d)) : this._enableIdb ? this._loadGltfFromBlob(e, i, o, s) : d ? this._loadGltfFromUrl(e, i, d.replace(i + ".gltf", "")) : Promise.reject()
    }
    loadSubsequence() {}
    loadVAT() {}
    getResourceName(e) {
        return this.meshTexList.get(e)
    }
    _loadGltfFromUrl(e, i, o) {
        return BABYLON.SceneLoader.LoadAssetContainerAsync(o, i + ".gltf", e.Scene, null, ".gltf")
    }
    _loadGlbFromBlob(e, i, o) {
        return e.urlTransformer(o).then(s=>BABYLON.SceneLoader.LoadAssetContainerAsync("", s, e.Scene, null, ".glb").then(c=>{
            if (c) {
                if (this.containers.get(o))
                    return c.dispose(),
                    this.containers.get(o);
                if (c.addAllToScene(),
                this.enableShareTexture && c.textures.length > 0) {
                    const d = [];
                    let _ = !1;
                    c.meshes.forEach(b=>{
                        if (b.material) {
                            const k = b.material._albedoTexture;
                            if (k) {
                                let j = k.name;
                                j = j.replace(" (Base Color)", "").split(".")[0];
                                const $ = this._sharedTex.get(j);
                                $ ? (_ = !0,
                                b.material._albedoTexture = $,
                                d.push($),
                                $._parentContainer = c,
                                $.xReferenceCount++) : (this._sharedTex.set(j, k),
                                c.textures[0].xReferenceCount = 1)
                            }
                        }
                    }
                    ),
                    _ && (c.textures.forEach(b=>{
                        e.Scene.removeTexture(b),
                        b.dispose()
                    }
                    ),
                    c.textures = d)
                }
                return c.xReferenceCount = 0,
                c.meshes.forEach(d=>{
                    d.setEnabled(!1)
                }
                ),
                this.containers.set(o, c),
                Promise.resolve(c)
            } else
                return Promise.reject("glb file load failed")
        }
        ))
    }
    _loadGlbFromUrl(e, i, o) {
        return BABYLON.SceneLoader.LoadAssetContainerAsync("", o, e.Scene, null, ".glb").then(s=>s ? (s.addAllToScene(),
        s.meshes.forEach(c=>{
            c.setEnabled(!1)
        }
        ),
        this.enableShareTexture && s.textures.length > 0 ? (s.meshes.forEach(c=>{
            if (c.material) {
                const d = c.material._albedoTexture;
                if (d) {
                    let _ = d.name;
                    _ = _.replace(" (Base Color)", "").split(".")[0];
                    const b = this._sharedTex.get(_);
                    b ? (c.material._albedoTexture = b,
                    b.xReferenceCount++) : (this._sharedTex.set(_, d),
                    s.textures[0].xReferenceCount = 1)
                }
            }
        }
        ),
        s.xReferenceCount = 0,
        this.containers.set(o, s),
        Promise.resolve(s)) : Promise.reject("glb file load failed"),
        s.xReferenceCount = 0,
        this.containers.set(o, s),
        Promise.resolve(s)) : Promise.reject("glb file load failed"))
    }
    _loadGltfFromBlob(e, i, o, s) {
        return new Promise((c,d)=>{
            const _ = [];
            let b = this._getSourceKey(i, o)
              , k = this.getGltfPath(b);
            if (!k && this.fillEmptyLod && (o = 0,
            b = this._getSourceKey(i, o),
            k = this.getGltfPath(b)),
            !k)
                return d(`[Engine] gltf path incorrect ${b},cancel.`);
            const j = this.mshPath.get(b + ".gltf");
            if (!j)
                return d("cannot find asset mshPath");
            const $ = this.binPath.get(b + ".bin");
            if (!$)
                return d("cannot find asset binPath");
            if (!s) {
                const tt = this.meshTexList.get(i);
                if (!tt || tt.length == 0)
                    return d("cannot find texture");
                s = tt[0]
            }
            const _e = this.texPath.get(s + ".png");
            if (!_e)
                return d();
            const et = this.texPath.get(s + "-astc.ktx");
            if (!et)
                return d();
            _.push(this._blobMapping(e, j)),
            _.push(this._blobMapping(e, $)),
            _.push(this._blobMapping(e, _e)),
            _.push(this._blobMapping(e, et)),
            Promise.all(_).then(()=>{
                const tt = k.replace(b + ".gltf", "");
                BABYLON.SceneLoader.LoadAssetContainerAsync(tt, b + ".gltf", e.Scene, null, ".gltf").then(rt=>{
                    var nt;
                    this.containers.set(k, rt),
                    rt.addAllToScene(),
                    rt.meshes.forEach(ot=>{
                        ot.setEnabled(!1)
                    }
                    );
                    const it = this._sharedTex.get(i);
                    it ? ((nt = rt.meshes[1].material._albedoTexture) == null || nt.dispose(),
                    rt.meshes[1].material._albedoTexture = it) : this._sharedTex.set(i, rt.meshes[1].material._albedoTexture),
                    c(rt)
                }
                )
            }
            )
        }
        )
    }
    _blobMapping(e, i) {
        return new Promise((o,s)=>{
            e.urlTransformer(i).then(c=>c ? (this._mappings.set(i, c),
            o(i)) : s(`[Engine] url urlTransformer parse error ${i}`))
        }
        )
    }
}

const avatarLoader = new XAvatarLoader();
export { avatarLoader };