
const { ccclass, property } = cc._decorator;
@ccclass
export default class HotUpdate extends cc.Component {
    @property({ type: cc.Node })
    public txtProgress: cc.Node = null;

    @property({ type: cc.Node })
    public txtProgressTips: cc.Node = null;

    @property({ type: cc.Node })
    public gProgress: cc.Node = null;

    @property({ type: cc.Node })
    public imgRole: cc.Node = null;

    @property({ type: cc.Node })
    public progress: cc.Node = null;

    @property({ type: cc.Sprite })
    public progressBar: cc.Sprite = null;

    @property({ type: cc.Asset })
    public manifestUrl: cc.Asset = null;

    _am: any;
    _checkListener: null;
    _updating: boolean;
    _updateListener: null;
    _canRetry: boolean;
    _failCount: number;
    _storagePath: string;
    versionCompareHandle: (versionA: any, versionB: any) => number;
    private _canEnterGame: boolean;

    public checkCb(event: any) {
        // cc.log('Code: ' + event.getEventCode());
        let isNewVersion = false;
        switch (event.getEventCode()) {
            case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
                console.log("No local manifest file found, hot update skipped.");
                break;
            case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
            case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
                console.log("Fail to download manifest file, hot update skipped.");
                break;
            case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
                console.log("Already up to date with the latest remote version.");
                this.EnterGame();
                break;
            case jsb.EventAssetsManager.NEW_VERSION_FOUND:
                console.log("New version found, please try to update.(" + this._am.getTotalBytes() + ')');
                isNewVersion = true;
                break;
            default:
                return;
        }

        this._am.setEventCallback(null);
        this._checkListener = null;
        this._updating = false;
        if (isNewVersion) {
            //有新版本，显示进度
            this.setProgress(0)
            this.hotUpdate();
            //5秒后判断是否开始下载，如果进度为0.直接进游戏，防止卡死
            this.scheduleOnce(() => {
                if (this.progressBar.fillRange == 0) {
                    this.EnterGame()
                }
            }, 10)
        }
    }

    setProgress(val: number) {
        this.gProgress.active = true
        this.progressBar.fillRange = val
        this.imgRole.x = -this.progress.width / 2 + val * this.progress.width
        this.txtProgress.getComponent(cc.Label).string = Math.floor(val * 100) + "%"
        this.txtProgressTips.getComponent(cc.Label).string = "资源更新中"
    }

    public updateCb(event: any) {
        var needRestart = false;
        var failed = false;
        // console.log("updateCb event:"+JSON.stringify(event));
        switch (event.getEventCode()) {
            case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
                // console.log('No local manifest file found, hot update skipped.');
                failed = true;
                break;
            case jsb.EventAssetsManager.UPDATE_PROGRESSION:
                // this.panel.byteProgress.progress = event.getPercent();
                // this.panel.fileProgress.progress = event.getPercentByFile();

                // this.panel.fileLabel.string = event.getDownloadedFiles() + ' / ' + event.getTotalFiles();
                // this.panel.byteLabel.string = event.getDownloadedBytes() + ' / ' + event.getTotalBytes();

                // this.pb_hotUpdate.progress =  event.getPercent();
                // this.lb_progress.string = Math.floor(event.getPercent()*100).toFixed(2) + '%';
                this.setProgress(event.getPercent())
                // var msg = event.getMessage();
                // if (msg) {
                //     cc.log(event.getPercent()/100 + '% : ' + msg);
                // }
                break;
            case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
            case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
                // console.log('Fail to download manifest file, hot update skipped.');
                failed = true;
                break;
            case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
                // console.log('Already up to date with the latest remote version.');
                failed = true;
                break;
            case jsb.EventAssetsManager.UPDATE_FINISHED:
                // console.log('Update finished. ' + event.getMessage());
                needRestart = true;
                break;
            case jsb.EventAssetsManager.UPDATE_FAILED:
                // console.log('Update failed. ' + event.getMessage());
                break;
            case jsb.EventAssetsManager.ERROR_UPDATING:
                // console.log('Asset update error: ' + event.getAssetId() + ', ' + event.getMessage());
                break;
            case jsb.EventAssetsManager.ERROR_DECOMPRESS:
                // console.log("ERROR_DECOMPRESS:"+event.getMessage());
                break;
            default:
                break;
        }

        if (failed) {
            this._am.setEventCallback(null);
            this._updateListener = null;
            this._updating = false;
            this.EnterGame();
        }

        if (needRestart) {
            this._am.setEventCallback(null);
            this._updateListener = null;
            // Prepend the manifest's search path
            var searchPaths = jsb.fileUtils.getSearchPaths();
            var newPaths = this._am.getLocalManifest().getSearchPaths();
            // console.log(JSON.stringify(newPaths));
            for (var i = 0; i < newPaths.length; i++) {
                if (searchPaths.indexOf(newPaths[i]) == -1) {
                    Array.prototype.unshift.apply(searchPaths, [newPaths[i]]);
                }
            }
            // This value will be retrieved and appended to the default search path during game startup,
            // please refer to samples/js-tests/main.js for detailed usage.
            // !!! Re-add the search paths in main.js is very important, otherwise, new scripts won't take effect.
            cc.sys.localStorage.setItem('HotUpdateSearchPaths', JSON.stringify(searchPaths));
            jsb.fileUtils.setSearchPaths(searchPaths);

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

    public retry() {
        if (!this._updating && this._canRetry) {
            this._canRetry = false;

            // console.log('Retry failed Assets...');
            this._am.downloadFailedAssets();
        }
    }

    public checkUpdate() {
        if (this._updating) {
            // console.log('Checking or updating ...');
            return;
        }
        if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
            // Resolve md5 url
            var url = this.manifestUrl.nativeUrl;
            if (cc.loader.md5Pipe) {
                url = cc.loader.md5Pipe.transformURL(url);
            }
            this._am.loadLocalManifest(url);
        }
        if (!this._am.getLocalManifest() || !this._am.getLocalManifest().isLoaded()) {
            console.log('Failed to load local manifest ...');
            this.EnterGame();
            return;
        }
        this.getLocalVersion()
        this._am.setEventCallback(this.checkCb.bind(this));

        this._am.checkUpdate();
        this._updating = true;
    }

    public hotUpdate() {
        if (this._am && !this._updating) {
            this._am.setEventCallback(this.updateCb.bind(this));

            if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
                // Resolve md5 url
                var url = this.manifestUrl.nativeUrl;
                if (cc.loader.md5Pipe) {
                    url = cc.loader.md5Pipe.transformURL(url);
                }
                this._am.loadLocalManifest(url);
            }
            this._failCount = 0;
            this._am.update();
            this._updating = true;
        }
    }

    // use this for initialization
    onLoad() {
        // Hot update is only available in Native build
        if (!cc.sys.isNative) {
            // this.EnterGame();
            return;
        }
        this._storagePath = ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'blackjack-remote-asset');
        cc.log('Storage path for remote asset : ' + this._storagePath);


        // Setup your own version compare handler, versionA and B is versions in string
        // if the return value greater than 0, versionA is greater than B,
        // if the return value equals 0, versionA equals to B,
        // if the return value smaller than 0, versionA is smaller than B.
        this.versionCompareHandle = function (versionA, versionB) {
            cc.log("JS Custom Version Compare: version A is " + versionA + ', version B is ' + versionB);
            var vA = versionA.split('.');
            var vB = versionB.split('.');
            for (var i = 0; i < vA.length; ++i) {
                var a = parseInt(vA[i]);
                var b = parseInt(vB[i] || 0);
                if (a === b) {
                    continue;
                }
                else {
                    return a - b;
                }
            }
            if (vB.length > vA.length) {
                return -1;
            }
            else {
                return 0;
            }
        };

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

        // Setup the verification callback, but we don't have md5 check function yet, so only print some message
        // Return true if the verification passed, otherwise return false
        this._am.setVerifyCallback(function (path, asset) {
            // When asset is compressed, we don't need to check its md5, because zip file have been deleted.
            var compressed = asset.compressed;
            // Retrieve the correct md5 value.
            var expectedMD5 = asset.md5;
            // asset.path is relative path and path is absolute.
            var relativePath = asset.path;
            // The size of asset file, but this value could be absent.
            var size = asset.size;
            if (compressed) {
                // console.log("Verification passed : " + relativePath);
                return true;
            }
            else {
                // console.log("Verification passed : " + relativePath + ' (' + expectedMD5 + ')');
                return true;
            }
        });

        // // console.log('Hot update is ready, please check or directly update.');

        if (cc.sys.os === cc.sys.OS_ANDROID) {
            // Some Android device may slow down the download process when concurrent tasks is too much.
            // The value may not be accurate, please do more test and find what's most suitable for your game.
            this._am.setMaxConcurrentTask(2);
            // console.log("Max concurrent tasks count have been limited to 2");
        }
        // cc.loader.loadRes('/project.manifest',(err,obj)=>{
        //     if (!err) {
        //         // let customManifestStr = JSON.stringify(obj);
        //         let _nativeAsset = JSON.parse(obj._nativeAsset);
        //         var customManifestStr = JSON.stringify({
        //             'packageUrl': _nativeAsset.packageUrl,
        //             'remoteManifestUrl': _nativeAsset.remoteManifestUrl,
        //             'remoteVersionUrl': _nativeAsset.remoteManifestUrl,
        //             'version': _nativeAsset.version,
        //             'assets': _nativeAsset.assert,
        //             'searchPaths': _nativeAsset.searchPaths
        //         });
        //         if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
        //             var manifest = new jsb.Manifest(customManifestStr, this._storagePath);
        //             this._am.loadLocalManifest(manifest, this._storagePath);
        //         }
        //     }
        // });
        //开始检测更新
        // this.checkUpdate();
    }

    public EnterGame() {
        // 游戏初始化
        this.getLocalVersion();
        this.scheduleOnce(() => {
            cc.director.loadScene("loading")
        }, 4)
        this._canEnterGame = true
    }

    //获取本地版本
    public getLocalVersion() {
        let localVersion = '';
        if (this._am && this._am.getLocalManifest() && !this._updating) {
            localVersion = this._am.getLocalManifest().getVersion();
            console.log("curVersion1:" + localVersion);
            import("Config" as any).then((cfgParam) => {
                let config = cfgParam.default
                config.appVersion = localVersion
                console.log("curVersion2:" + localVersion);
            })
            // console.log("curVersion:"+localVersion);
        }
        //return localVersion;
    }

    onDestroy() {
        if (this._updateListener) {
            this._am.setEventCallback(null);
            this._updateListener = null;
        }
    }
};
