
/*
loader.js：负责对资源的加载

 */
"use strict";

function loader () {
    this._init();
}

loader.prototype._init = function () {

}

////// 设置加载进度条进度 //////
loader.prototype._setStartProgressVal = function (val) {
    core.dom.startTopProgress.style.width = val + '%';
}

////// 设置加载进度条提示文字 //////
loader.prototype._setStartLoadTipText = function (text) {
    core.dom.startTopLoadTips.innerText = text;
}

loader.prototype._load = function (callback) {
    this._loadMusics();
    if (main.useCompress) {
        this._load_async(callback);
    } else {
        this._load_sync(callback);
    }
}

loader.prototype._load_sync = function (callback) {
    this._loadAnimates_sync();
    this._loadSounds_sync();
    core.loader._loadMaterials_sync(function () {
        core.loader._loadExtraImages_sync(function () {
            core.loader._loadAutotiles_sync(function () {
                core.loader._loadTilesets_sync(callback);
            })
        })
    });
}

loader.prototype._load_async = function (callback) {
    core.loader._setStartLoadTipText('正在加载资源文件...');
    const all = {};

    const _makeOnProgress = function (name) {
        if (!all[name]) all[name] = { loaded: 0, total: 0, finished: false };
        return (loaded, total) => {
            all[name].loaded = loaded;
            all[name].total = total;
            let allLoaded = 0, allTotal = 0;
            for (const one of Object.values(all)) {
                allLoaded += one.loaded;
                allTotal += one.total;
            }
            console.log(allLoaded. allTotal);
            if (allTotal > 0) {
                if (allLoaded == allTotal) {
                    core.loader._setStartLoadTipText("正在处理资源文件... 请稍候...");
                } else {
                    core.loader._setStartLoadTipText('正在加载资源文件... ' +
                        core.formatSize(allLoaded) + " / " + core.formatSize(allTotal) +
                        " (" + (allLoaded / allTotal * 100).toFixed(2) + "%)");
                }
                core.loader._setStartProgressVal(allLoaded / allTotal * 100);
            }
        };
    }
    const _makeOnFinished = function (name) {
        return () => {
            setTimeout(() => {
                all[name].finished = true;
                for (var one in all) {
                    if (!all[one].finished) return;
                }
                callback();
            });
        }
    }

    if (main.splitChunkMap) {
        this._loadAnimates_chunked(main.splitChunkMap.animates, _makeOnProgress, _makeOnFinished);
        this._loadSounds_chunked(main.splitChunkMap.sounds, _makeOnProgress, _makeOnFinished);
        this._loadMaterials_chunked(main.splitChunkMap.materials, _makeOnProgress, _makeOnFinished);
        this._loadExtraImages_chunked(main.splitChunkMap.images, _makeOnProgress, _makeOnFinished);
        this._loadAutotiles_chunked(main.splitChunkMap.autotiles, _makeOnProgress, _makeOnFinished);
        this._loadTilesets_chunked(main.splitChunkMap.tilesets, _makeOnProgress, _makeOnFinished);
    } else {
        this._loadAnimates_async(_makeOnProgress('animates'), _makeOnFinished('animates'));
        this._loadSounds_async(_makeOnProgress('sounds'), _makeOnFinished('sounds'));
        this._loadMaterials_async(_makeOnProgress('materials'), _makeOnFinished('materials'));
        this._loadExtraImages_async(_makeOnProgress('images'), _makeOnFinished('images'));
        this._loadAutotiles_async(_makeOnProgress('autotiles'), _makeOnFinished('autotiles'));
        this._loadTilesets_async(_makeOnProgress('tilesets'), _makeOnFinished('tilesets'));
    }
}

// ----- 加载资源文件 ------ //

loader.prototype._loadMaterials_sync = function (callback) {
    this._setStartLoadTipText("正在加载资源文件...");
    this.loadImages("materials", core.materials, core.material.images, function () {
        core.loader._loadMaterials_afterLoad();
        callback();
    });
}

loader.prototype._loadMaterials_async = function (onprogress, onfinished) {
    this.loadImagesFromZip('project/materials/materials.h5data', core.materials, core.material.images, onprogress, function () {
        core.loader._loadMaterials_afterLoad();
        onfinished();
    });
}

loader.prototype._loadMaterials_chunked = async function (chunks, makeOnProgress, makeOnFinished) {
    await this._loadImagesFromChunks(chunks, core.materials, core.material.images, makeOnProgress, makeOnFinished);
    core.loader._loadMaterials_afterLoad();
}

loader.prototype._loadMaterials_afterLoad = function () {
    const images = core.splitImage(core.material.images['icons']);
    for (let key in core.statusBar.icons) {
        if (typeof core.statusBar.icons[key] == 'number') {
            core.statusBar.icons[key] = images[core.statusBar.icons[key]];
            if (core.statusBar.image[key] != null)
                core.statusBar.image[key].src = core.statusBar.icons[key].src;
        }
    }
}

// ------ 加载使用的图片 ------ //

loader.prototype._loadExtraImages_sync = function (callback) {
    core.material.images.images = {};
    this._setStartLoadTipText("正在加载图片文件...");
    core.loadImages("images", core.images, core.material.images.images, callback);
}

loader.prototype._loadExtraImages_async = function (onprogress, onfinished) {
    core.material.images.images = {};
    
    // Check .gif
    const gifs = images.filter(function (name) {
        return name.toLowerCase().endsWith('.gif');
    });
    // gif没有被压缩在zip中，延迟加载...
    this._loadExtraImages_loadLazy(gifs);

    images = images.filter((name) => !name.toLowerCase().endsWith('.gif'));

    this.loadImagesFromZip('project/images/images.h5data', images, core.material.images.images, onprogress, onfinished);
}

loader.prototype._loadExtraImages_chunked = function (chunks, makeOnProgress, makeOnFinished) {
    core.material.images.images = {};
    let images = core.images;

    // Check .gif
    const gifs = images.filter(function (name) {
        return name.toLowerCase().endsWith('.gif');
    });
    // gif没有被压缩在zip中，延迟加载...
    this._loadExtraImages_loadLazy(gifs);

    images = images.filter((name) => !name.toLowerCase().endsWith('.gif'));

    this._loadImagesFromChunks(chunks, images, core.material.images.images, makeOnProgress, makeOnFinished);
}

loader.prototype._loadExtraImages_loadLazy = function (list) {
    list.forEach(function (gif) {
        this.loadImage("images", gif, (id, image) => {
            if (image != null) {
                core.material.images.images[gif] = image;
            }
        });
    }, this);
}

// ------ 加载自动元件 ------ //

loader.prototype._loadAutotiles_sync = function (callback) {
    core.material.images.autotile = {};
    var keys = Object.keys(core.material.icons.autotile);
    var autotiles = {};

    this._setStartLoadTipText("正在加载自动元件...");
    this.loadImages("autotiles", keys, autotiles, function () {
        core.loader._loadAutotiles_afterLoad(keys, autotiles);
        callback();
    });
}

loader.prototype._loadAutotiles_async = function (onprogress, onfinished) {
    core.material.images.autotile = {};
    var keys = Object.keys(core.material.icons.autotile);
    var autotiles = {};

    this.loadImagesFromZip('project/autotiles/autotiles.h5data', keys, autotiles, onprogress, function () {
        core.loader._loadAutotiles_afterLoad(keys, autotiles);
        onfinished();
    });
}

loader.prototype._loadAutotiles_chunked = async function (chunks, makeOnProgress, makeOnFinished) {
    core.material.images.autotile = {};
    const keys = Object.keys(core.material.icons.autotile);
    const autotiles = {};

    await this._loadImagesFromChunks(chunks, keys, autotiles, makeOnProgress, makeOnFinished);
    core.loader._loadAutotiles_afterLoad(keys, autotiles);
}

loader.prototype._loadAutotiles_afterLoad = function (keys, autotiles) {
    // autotile需要保证顺序
    keys.forEach(function (v) {
        core.material.images.autotile[v] = autotiles[v];
    });

    setTimeout(function () {
        core.maps._makeAutotileEdges();
    });

}

// ------ 加载额外素材 ------ //

loader.prototype._loadTilesets_sync = function (callback) {
    core.material.images.tilesets = {};
    this._setStartLoadTipText("正在加载额外素材...");
    this.loadImages("tilesets", core.tilesets, core.material.images.tilesets, function () {
        core.loader._loadTilesets_afterLoad();
        callback();
    });
}

loader.prototype._loadTilesets_async = function (onprogress, onfinished) {
    core.material.images.tilesets = {};
    this.loadImagesFromZip('project/tilesets/tilesets.h5data', core.tilesets, core.material.images.tilesets, onprogress, function () {
        core.loader._loadTilesets_afterLoad();
        onfinished();
    });
}

loader.prototype._loadTilesets_chunked = async function (chunks, makeOnProgress, makeOnFinished) {
    core.material.images.tilesets = {};
    await this._loadImagesFromChunks(chunks, core.tilesets, core.material.images.tilesets, makeOnProgress, makeOnFinished);
    core.loader._loadTilesets_afterLoad();
}

loader.prototype._loadTilesets_afterLoad = function () {
    // 检查宽高是32倍数，如果出错在控制台报错
    for (var imgName in core.material.images.tilesets) {
        var img = core.material.images.tilesets[imgName];
        if (img.width % 32 != 0 || img.height % 32 != 0) {
            console.warn("警告！" + imgName + "的宽或高不是32的倍数！");
        }
        if (img.width * img.height > 32 * 32 * 3000) {
            console.warn("警告！" + imgName + "上的图块素材个数大于3000！");
        }
    }
}

// ------ 实际加载一系列图片 ------ //

loader.prototype.loadImages = function (dir, names, toSave, callback) {
    if (!names || names.length == 0) {
        if (callback) callback();
        return;
    }
    var items = 0;
    for (var i = 0; i < names.length; i++) {
        this.loadImage(dir, names[i], function (id, image) {
            core.loader._setStartLoadTipText('正在加载图片 ' + id + "...");
            if (toSave[id] !== undefined) {
                if (image != null)
                    toSave[id] = image;
                return;
            }
            toSave[id] = image;
            items++;
            core.loader._setStartProgressVal(items * (100 / names.length));
            if (items == names.length) {
                if (callback) callback();
            }
        })
    }
}

loader.prototype.loadImage = function (dir, imgName, callback) {
    try {
        var name = imgName;
        if (name.indexOf(".") < 0)
            name = name + ".png";
        var image = new Image();
        image.onload = function () {
            image.setAttribute('_width', image.width);
            image.setAttribute('_height', image.height);
            callback(imgName, image);
        }
        image.onerror = function () {
            callback(imgName, null);
        }
        image.src = 'project/' + dir + '/' + name + "?v=" + main.version;
        if (name.endsWith('.gif'))
            callback(imgName, null);
    }
    catch (e) {
        console.error(e);
    }
}

// ------ 从zip中加载一系列图片 ------ //

loader.prototype.loadImagesFromZip = function (url, names, toSave, onprogress, onfinished) {
    if (!names || names.length == 0) {
        if (onfinished) onfinished();
        return;
    }

    core.unzip(url + "?v=" + main.version, function (data) {
        var cnt = 1;
        names.forEach(function (name) {
            var imgName = name;
            if (imgName.indexOf('.') < 0) imgName += '.png';
            if (imgName in data) {
                var img = new Image();
                var url = URL.createObjectURL(data[imgName]);
                cnt++;
                img.onload = function () {
                    cnt--;
                    URL.revokeObjectURL(url);
                    img.setAttribute('_width', img.width);
                    img.setAttribute('_height', img.height);
                    if (cnt == 0 && onfinished) onfinished();
                }
                img.src = url;
                toSave[name] = img;
            }
        });
        cnt--;
        if (cnt == 0 && onfinished) onfinished();
    }, null, false, onprogress);
}

loader.prototype._loadImagesFromChunks = async function (chunks, names, toSave, makeOnProgress, makeOnFinished) {
    await Promise.all(chunks.map((chunk) => {
        const onfinished = makeOnFinished(chunk);
        const onprogress = makeOnProgress(chunk);
        return new Promise((resolve) => {
            this.loadImagesFromZip(chunk, names, toSave, onprogress, () => {
                onfinished();
                resolve();     
            });
        });
    }));
}

// ------ 加载动画文件 ------ //

loader.prototype._loadAnimates_sync = function () {
    this._setStartLoadTipText("正在加载动画文件...");

    if (main.supportBunch) {
        if (core.animates.length > 0) {
            core.http('GET', '__all_animates__?v=' + main.version + '&id=' + core.animates.join(','), null, function (content) {
                var u = content.split('@@@~~~###~~~@@@');
                for (var i = 0; i < core.animates.length; ++i) {
                    if (u[i] != '') {
                        core.material.animates[core.animates[i]] = core.loader._loadAnimate(u[i]);
                    } else {
                        console.error('无法找到动画文件' + core.animates[i] + '！');
                    }
                }
            }, "text/plain; charset=x-user-defined");
        }
        return;
    }

    core.animates.forEach(function (t) {
        core.http('GET', 'project/animates/' + t + ".animate?v=" + main.version, null, function (content) {
            core.material.animates[t] = core.loader._loadAnimate(content);
        }, function (e) {
            console.error(e);
            core.material.animates[t] = null;
        }, "text/plain; charset=x-user-defined")
    });
}

loader.prototype._loadAnimates_async = function (onprogress, onfinished) {
    this._loadFileFromZip('project/animates/animates.h5data', this._saveAnimate, true, onprogress, onfinished);
}

loader.prototype._loadAnimates_chunked = function (chunks, makeOnProgress, makeOnFinished) {
    this._loadFileFromChunks(chunks, this._saveAnimate, true, makeOnProgress, makeOnFinished);
}

loader.prototype._saveAnimate = function (animates, onfinished) {
    for (var name in animates) {
        if (name.endsWith(".animate")) {
            var t = name.substring(0, name.length - 8);
            if (core.animates.indexOf(t) >= 0)
                core.material.animates[t] = core.loader._loadAnimate(animates[name]);
        }
    }
    onfinished();
}

loader.prototype._loadAnimate = function (content) {
    try {
        content = JSON.parse(content);
        var data = {};
        data.ratio = content.ratio;
        data.se = content.se;
        data.pitch = content.pitch;
        data.images = [];
        content.bitmaps.forEach(function (t2) {
            if (!t2) {
                data.images.push(null);
            }
            else {
                try {
                    var image = new Image();
                    image.src = t2;
                    data.images.push(image);
                } catch (e) {
                    console.error(e);
                    data.images.push(null);
                }
            }
        })
        data.frame = content.frame_max;
        data.frames = [];
        content.frames.forEach(function (t2) {
            var info = [];
            t2.forEach(function (t3) {
                info.push({
                    'index': t3[0],
                    'x': t3[1],
                    'y': t3[2],
                    'zoom': t3[3],
                    'opacity': t3[4],
                    'mirror': t3[5] || 0,
                    'angle': t3[6] || 0,
                })
            })
            data.frames.push(info);
        });
        return data;
    }
    catch (e) {
        console.error(e);
        return null;
    }
}

// ------ 加载音乐和音效 ------ //

loader.prototype._loadMusics = function () {
    core.bgms.forEach(function (t) {
        core.loader.loadOneMusic(t);
    });
    // 直接开始播放
    core.playBgm(main.startBgm);
}

loader.prototype._loadSounds_sync = function () {
    this._setStartLoadTipText("正在加载音效文件...");
    core.sounds.forEach(function (t) {
        core.loader.loadOneSound(t);
    });
}

loader.prototype._loadSounds_async = function (onprogress, onfinished) {
    this._loadFileFromZip('project/sounds/sounds.h5data', this._saveSounds, false, onprogress, onfinished);
}

loader.prototype._loadSounds_chunked = function (chunks, makeOnProgress, makeOnFinished) {
    this._loadFileFromChunks(chunks, this._saveSounds, false, makeOnProgress, makeOnFinished);
}

loader.prototype._saveSounds = function (data, onfinished) {
    // 延迟解析
    setTimeout(function () {
        for (var name in data) {
            if (core.sounds.indexOf(name) >= 0) {
                core.loader._loadOneSound_decodeData(name, data[name]);
            }
        }
        onfinished();
    });
}

loader.prototype.loadOneMusic = function (name) {
    var music = new Audio();
    music.preload = 'none';
    if (main.bgmRemote) music.src = main.bgmRemoteRoot + core.firstData.name + '/' + name;
    else music.src = 'project/bgms/' + name;
    music.loop = 'loop';
    core.material.bgms[name] = music;
}

loader.prototype.loadOneSound = function (name) {
    core.http('GET', 'project/sounds/' + name + "?v=" + main.version, null, function (data) {
        core.loader._loadOneSound_decodeData(name, data);
    }, function (e) {
        console.error(e);
        core.material.sounds[name] = null;
    }, null, 'arraybuffer');
}

loader.prototype._loadOneSound_decodeData = function (name, data) {
    if (data instanceof Blob) {
        var blobReader = new zip.BlobReader(data);
        blobReader.init(function () {
            blobReader.readUint8Array(0, blobReader.size, function (uint8) {
                core.loader._loadOneSound_decodeData(name, uint8.buffer);
            })
        });
        return;
    }
    try {
        core.musicStatus.audioContext.decodeAudioData(data, function (buffer) {
            core.material.sounds[name] = buffer;
        }, function (e) {
            console.error(e);
            core.material.sounds[name] = null;
        })
    }
    catch (e) {
        console.error(e);
        core.material.sounds[name] = null;
    }
}

loader.prototype.loadBgm = function (name) {
    name = core.getMappedName(name);
    if (!core.material.bgms[name]) return;
    // 如果没开启音乐，则不预加载
    if (!core.musicStatus.bgmStatus) return;
    // 是否已经预加载过
    var index = core.musicStatus.cachedBgms.indexOf(name);
    if (index >= 0) {
        core.musicStatus.cachedBgms.splice(index, 1);
    }
    else {
        // 预加载BGM
        this._preloadBgm(core.material.bgms[name]);
        // core.material.bgms[name].load();
        // 清理尾巴
        if (core.musicStatus.cachedBgms.length == core.musicStatus.cachedBgmCount) {
            this.freeBgm(core.musicStatus.cachedBgms.pop());
        }
    }
    // 移动到缓存最前方
    core.musicStatus.cachedBgms.unshift(name);
}

loader.prototype._preloadBgm = function (bgm) {
    bgm.volume = 0;
    bgm.play();
}

loader.prototype.freeBgm = function (name) {
    name = core.getMappedName(name);
    if (!core.material.bgms[name]) return;
    // 从cachedBgms中删除
    core.musicStatus.cachedBgms = core.musicStatus.cachedBgms.filter(function (t) {
        return t != name;
    });
    // 清掉缓存
    core.material.bgms[name].removeAttribute("src");
    core.material.bgms[name].load();
    core.material.bgms[name] = null;
    if (name == core.musicStatus.playingBgm) {
        core.musicStatus.playingBgm = null;
    }
    // 三秒后重新加载
    setTimeout(function () {
        core.loader.loadOneMusic(name);
    }, 3000);
}

loader.prototype._loadFileFromZip = function (url, save, convertToText, onprogress, onfinished) {
    core.unzip(url + '?v=' + main.version, function (data) {
        save(data, onfinished);
    }, null, convertToText, onprogress);
}

loader.prototype._loadFileFromChunks = async function (chunks, save, convertToText, makeOnProgress, makeOnFinished) {
    await Promise.all(chunks.map((chunk) => {
        const onfinished = makeOnFinished(chunk);
        const onprogress = makeOnProgress(chunk);
        return new Promise((resolve) => {
            this._loadFileFromZip(chunk, save, convertToText, onprogress, () => {
                onfinished();
                resolve();
            });
        });
    }));
}
