const hotFixData = require("HotFixData");

var HotUpdate = cc.Class({
    extends: cc.Component,

    properties: {
        manifestUrl: {
            type: cc.Asset,
            default: null,
        },
        _updating: false,
        _canRetry: false,
        _storagePath: "",
        callBack: null,
    },

    statics: {
        instance: null,
    },

    checkCb(event) {
        let gohotUpdate = false;
        switch (event.getEventCode()) {
            case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
                hotFixData.info = "(1/2)没有找到本地更新文件, 跳过更新";
                hotFixData.progress = 1;
                break;
            case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
            case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
                hotFixData.info = "(1/2)下载更新文件失败, 跳过更新";
                hotFixData.progress = 1;
                break;
            case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
                hotFixData.info = "(1/2)已经是最新版本，正在进入游戏";
                hotFixData.progress = 1;
                break;
            case jsb.EventAssetsManager.NEW_VERSION_FOUND:
                hotFixData.info = "(1/2)发现新版本";
                cc.log(hotFixData.info);
                gohotUpdate = true;
                break;
            default:
                return;
        }
        this._am.setEventCallback(null);
        this._checkListener = null;
        this._updating = false;
        if (gohotUpdate) {
            this.hotUpdate();
        }
    },

    updateCb(event) {
        let needRestart = false;
        let failed = false;

        switch (event.getEventCode()) {
            case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
                hotFixData.info = "(1/2)没有找到本地更新文件, 跳过更新";
                hotFixData.progress = 1;
                failed = true;
                break;
            case jsb.EventAssetsManager.UPDATE_PROGRESSION: {
                const totalMbs = (event.getTotalBytes() / 1024 / 1024).toFixed(2);
                const downloadMbs = (event.getDownloadedBytes() / 1024 / 1024).toFixed(2);
                hotFixData.info = "(1/2)正在更新资源...";
                if (downloadMbs && totalMbs) {
                    hotFixData.fileProgress = `${downloadMbs}MB/${totalMbs}MB(${Math.floor(downloadMbs / totalMbs * 100)}%)`;
                }
                if (!Math.floor(downloadMbs / totalMbs * 100)) {
                    hotFixData.fileProgress = `${downloadMbs}MB/${totalMbs}MB(0%)`;
                }
                hotFixData.progress = event.getPercent();
                break;
            }
            case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
            case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
                hotFixData.info = "(1/2)没有找到本地更新文件, 跳过更新";
                hotFixData.progress = 1;
                failed = true;
                break;
            case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
                hotFixData.info = "(1/2)已是最新版本，正在进入游戏";

                hotFixData.progress = 1;
                hotFixData.fileProgress = "";
                failed = true;
                break;
            case jsb.EventAssetsManager.UPDATE_FINISHED:
                hotFixData.info = "(1/2)更新完成，正在重新启动游戏";
                // hotFixData.progress = 1;
                hotFixData.fileProgress = "";
                needRestart = true;
                break;
            case jsb.EventAssetsManager.UPDATE_FAILED:
                hotFixData.info = `(1/2)更新失败${event.getMessage()}`;
                // hotFixData.progress = 1;
                hotFixData.fileProgress = "";
                this._updating = false;
                this._canRetry = true;
                break;
            case jsb.EventAssetsManager.ERROR_UPDATING:
                hotFixData.info = `(1/2)资源更新出错 ${event.getAssetId()}, ${event.getMessage()}`;
                break;
            case jsb.EventAssetsManager.ERROR_DECOMPRESS:
                hotFixData.info = "(1/2)解压文件出错";
                break;
            default:
                break;
        }
        cc.log(hotFixData.info);
        if (failed) {
            this._am.setEventCallback(null);
            this._updateListener = null;
            this._updating = false;
        }

        if (needRestart) {
            this._am.setEventCallback(null);
            this._updateListener = null;
            // Prepend the manifest's search path
            let searchPaths = jsb.fileUtils.getSearchPaths();
            const newPaths = this._am.getLocalManifest().getSearchPaths();
            Array.prototype.unshift.apply(searchPaths, newPaths);
            cc.sys.localStorage.setItem("HotUpdateSearchPaths", JSON.stringify(searchPaths));
            jsb.fileUtils.setSearchPaths(searchPaths);

            cc.audioEngine.stopAll();
            cc.game.restart();
        }

        if (this._canRetry) {
            this.retry();
        }
    },

    retry() {
        if (!this._updating && this._canRetry) {
            if (this._failCount++ <= 3) {
                this._canRetry = false;
                // '重试更新资源...';
                this._am.downloadFailedAssets();
            } else {
                // 删除temp文件夹，重新更新资源
                cc.log(`=====removeDirectory====${this._storagePath}_temp/`);
                jsb.fileUtils.removeDirectory(`${this._storagePath}_temp/`);
                this.hotUpdate();
            }
        }
    },

    checkUpdate() {
        if (this._updating) {
            hotFixData.info = "正在更新...";
            return;
        }
        this._am.setEventCallback(this.checkCb.bind(this));
        this._am.checkUpdate();
    },

    hotUpdate() {
        if (this._am && !this._updating) {
            if (this._updateListener) {
                this._am.setEventCallback(null);
                this._updateListener = null;
            }

            this._am.setEventCallback(this.updateCb.bind(this));

            if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
                this.loadManifest();
            }

            this._failCount = 0;
            hotFixData.info = "开始更新...";
            this._am.update();
            this._updating = true;
        }
    },

    // use this for initialization
    goUpdate() {
        // Hot update is only available in Native build
        if (!cc.sys.isNative) {
            hotFixData.info = "must native";
            return;
        }

        this._storagePath = (`${jsb.fileUtils ? jsb.fileUtils.getWritablePath() : "/"}remote-assets-wayne/`);

        this.versionCompareHandle = (versionA, versionB) => {
            cc.log(`utils versionCompareHandle Custom Version Compare: version A is ${versionA}, version B is ${versionB}`);
            const vA = versionA.split(".");
            const vB = versionB.split(".");
            for (let i = 0; i < vA.length; i++) {
                const a = parseInt(vA[i], 10);
                const b = parseInt(vB[i] || 0, 10);
                if (!(a === b)) {
                    return a - b;
                }
            }
            if (vB.length > vA.length) {
                return -1;
            }
            return 0;
        };

        // Init with empty manifest url for testing custom manifest
        this._am = new jsb.AssetsManager("", this._storagePath, this.versionCompareHandle);

        this._am.setVerifyCallback((path, asset) => {
            const { compressed } = asset;
            const expectedMD5 = asset.md5;
            const relativePath = asset.path;
            cc.log(`setVerifyCallback:${relativePath}${asset.path}`);
            const { size } = asset;
            if (compressed) {
                // hotFixData.info = "解压文件";
                return true;
            }
            // hotFixData.info = "下载文件";
            return true;
        });

        this.loadManifest();

        hotFixData.info = "(1/2)正在检查更新...";
        cc.log(hotFixData.info);
        if (cc.sys.os === cc.sys.OS_ANDROID) {
            this._am.setMaxConcurrentTask(2);
        }
        hotFixData.info = "(1/2)开始检查版本信息";
        this.hotUpdate();
    },

    loadManifest() {
        const UIRLFILE = `http://18.162.159.75:7480/pre-application/wayne/1/HotUpdate/remote-assets-wayne/`;
        const remoteManifestUrl = `${this._storagePath}project.manifest`;
        if (this.manifestUrl) {
            this.manifestUrl = null;
        }

        const customManifestStr = JSON.stringify({
            packageUrl: UIRLFILE,
            remoteManifestUrl: `${UIRLFILE}project.manifest`,
            remoteVersionUrl: `${UIRLFILE}version.manifest`,
            version: "0",
            assets: {},
            searchPaths: [],
        });
        cc.log(`Storage path for remote asset : ${this._storagePath}`);
        if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
            if (jsb.fileUtils.isFileExist(remoteManifestUrl)) {
                cc.log("加载本地Manifest");
                this._am.loadLocalManifest(remoteManifestUrl);
            } else if (this.manifestUrl) {
                if (!this._am.getLocalManifest() || !this._am.getLocalManifest().isLoaded()) {
                    cc.log("缓存路径manifest无效，使用资源里面的");
                    let url = this.manifestUrl.nativeUrl;
                    cc.log(`${url}`);
                    if (cc.loader.md5Pipe) {
                        url = cc.loader.md5Pipe.transformURL(url);
                    }
                    this._am.loadLocalManifest(url);
                }
            } else if (!this._am.getLocalManifest() || !this._am.getLocalManifest().isLoaded()) {
                cc.log("加载网络Manifest");
                const manifest = new jsb.Manifest(customManifestStr, this._storagePath);
                cc.log(manifest);
                this._am.loadLocalManifest(manifest, this._storagePath);
            }
        }
    },

    onDestroy() {
        cc.log("HotUpate onDestory");
        if (this._updateListener) {
            this._am.setEventCallback(null);
            this._updateListener = null;
        }
    },
});

HotUpdate.getInstance = () => {
    if (!HotUpdate.instance) {
        HotUpdate.instance = new HotUpdate();
    }
    return HotUpdate.instance;
};

module.exports = HotUpdate;
