const {ccclass, property} = cc._decorator;

/**
 * 热更新
 */
@ccclass
export default class HotUpdate extends cc.Component {
   
    @property(cc.Asset)
    public manifestUrl:cc.Asset = null;

    @property(cc.Node)
    public gxNode:cc.Node = null;//更新load

    // @property(cc.Label)
    // public mInfo:cc.Label = null;

    @property(cc.Node)
    public mMyNode:cc.Node = null;

    @property(cc.Label)
    public mV1:cc.Label = null;

    @property(cc.Label)
    public mV2:cc.Label = null;

    @property(cc.Label)
    public mLoadBfb:cc.Label = null;

    @property(cc.Label)
    public fileCount:cc.Label = null;//文件数量

    @property(cc.ProgressBar)
    public byteProgress:cc.ProgressBar = null;//下载进度

    // @property(cc.Node)
    // mLogContent:cc.Node = null;//更新日志

    private _updating:boolean = false;
    private _am:jsb.AssetsManager = null;//jsb.AssetsManager
    private _canRetry:boolean = false;//是否可以重新下载

    private _storagePath:string;//存储路径
    private versionCompareHandle: (versionA: string, versionB: string) => number = null!;

    public login(){
        if(cc.sys.isNative){
            // cc.director.loadScene('anysdkLogin');
            cc.director.loadScene('main');
        }else{
            cc.director.loadScene('main');
        }
    }
    public exitGame(){
        cc.game.end();
    }
    public onLoad(){
        let proto = cc.ProgressBar.prototype;
        let updateBarStatus = proto['_updateBarStatus'];
        proto['_updateBarStatus'] = function () {
            if (this.progress == 0) {
                this.progress = 0.001;
            }
            updateBarStatus.bind(this)();
        }


        this.mV1.node.active = false
        this.mV2.node.active = false
        cc.game.setFrameRate(35);
        this.gxNode.active = false;
        if (!cc.sys.isNative) {
            this.login();
            return;
        }
        this._storagePath = ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'mmtg-remote-asset');
        cc.log('Storage path for remote asset : ' + this._storagePath);
        let myself = this;
        this.versionCompareHandle = function (versionA: string, versionB: string) {
            console.log("JS Custom Version Compare: version A is " + versionA + ', version B is ' + versionB);
            myself.mV1.node.active = true
            myself.mV2.node.active = true
            myself.mV1.string = '客户端版本:'+versionA
            myself.mV2.string = '服务器版本:'+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((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) {
                // this.mInfo.string = "Verification passed : " + relativePath;
                return true;
            }else {
                // this.mInfo.string = "Verification passed : " + relativePath + ' (' + expectedMD5 + ')';
                // if(!jsb.fileUtils.isFileExist(path)){
                //     return false;
                // }
                // let dataBinary = jsb.fileUtils.getDataFromFile(path);
                // let buffer = nodeJsBufferTool.from(dataBinary);
                // let md5Code = crypto.createHash('md5').update(buffer).digest('hex');
                // if(md5Code !== asset.md5){
                //     cc.log('不一致文件:',path);
                //     cc.log('md5Code = ',md5Code);
                //     cc.log('asset   = ',asset.md5);
                //     // cc.log('dataBinary : ',dataBinary);
                //     // cc.log('strData : ',strData);
                //     return false;
                // }
                // cc.log('一致文件 :',path);
                // return true;
                 // this.mInfo.string = "Verification passed : " + relativePath + ' (' + expectedMD5 + ')';
                 if(!jsb.fileUtils.isFileExist(path)){
                    return false;
                }
                let fileSize = jsb.fileUtils.getFileSize(path);
                
                if(fileSize != asset.size){
                    return false;
                }
                return true;
            }
        });

        // this.mInfo.string = '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(10);
            // this.mInfo.string = "Max concurrent tasks count have been limited to 2";
        }else{
            this._am.setMaxConcurrentTask(15);
        }
        
        this.fileCount.string = '';
        this.mLoadBfb.string = '';
        this.byteProgress.progress = 0;

        //判断是否需要下载新包更新
        //1.获取app版本
        //2.获取服务器最新版本
        //3.打开下载页
        // if (cc.sys.os === cc.sys.OS_ANDROID) {
        //     this.getServerAppVersion((v1)=>{
        //         if(v1){
        //             let v2 = this.getAppVersion();
        //             let state = this.versionCompareHandle(v1,v2);
        //             if(state > 0){
        //                 //如果有新版本
        //                 this.updateBtn.active = true;
        //             }else{
        //                 this.checkUpdate();
        //             }
        //         }else{
        //             this.checkUpdate();
        //         }
        //     });
        // }else{
        //     this.checkUpdate();
        // }
        this.checkUpdate();
    }
    public checkUpdate() {
        if (this._updating) {
            // cc.log('Checking or updating ...');
            this.pushLog('Checking or updating ...');
            return;
        }
        if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
            var url = this.manifestUrl.nativeUrl;
            this._am.loadLocalManifest(url);
        }
        if (!this._am.getLocalManifest() || !this._am.getLocalManifest().isLoaded()) {
            // cc.log('Failed to load local manifest ...');
            this.pushLog('Failed to load local manifest ...');
            return;
        }
        this._am.setEventCallback(this.checkCb.bind(this));

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

    public hotUpdate () {
        this.gxNode.active = true;
        this.fileCount.string = 'loading...';
        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;
                if (cc.loader.md5Pipe) {
                    url = cc.loader.md5Pipe.transformURL(url);
                }
                this._am.loadLocalManifest(url);
            }
            this._am.update();
            this._updating = true;
        }
    }

    public checkCb(event){
        cc.log('Code: ' + event.getEventCode());
        switch (event.getEventCode())
        {
            case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
                cc.log("No local manifest file found, hot update skipped.");
                this.login();
                break;
            case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
            case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
                cc.log("Fail to download manifest file, hot update skipped.");
                this.login();
                break;
            case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
                cc.log("Already up to date with the latest remote version.");
                this.login();
                break;
            case jsb.EventAssetsManager.NEW_VERSION_FOUND:
                cc.log("New version found, please try to update.");
                // this.panel.fileProgress.progress = 0;
                // this.panel.byteProgress.progress = 0;
                // this.updateBtn.active = true;
                // cc.log('getTotalBytes : ',event.getTotalBytes());
                this._am.setEventCallback(null);
                this._updating = false;
                this.hotUpdate();
                break;
            default:
                return;
        }
    }

    public updateCb(event) {
        var needRestart = false;
        var failed = false;
        switch (event.getEventCode())
        {
            case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
                cc.log('No local manifest file found, hot update skipped.');
                failed = true;
                break;
            case jsb.EventAssetsManager.UPDATE_PROGRESSION:
                // this.byteProgress.progress = event.getPercent();
                let percentByFile = event.getPercentByFile();
                if(!percentByFile){
                    percentByFile = 0;
                }else{
                    if(event.getDownloadedFiles() < event.getTotalFiles()-1){
                        this.fileCount.string = event.getDownloadedFiles() + ' / ' + event.getTotalFiles();
                    }else{
                        this.fileCount.string = '文件解压中...';
                    }
                }
                this.byteProgress.progress = percentByFile;

                this.mMyNode.x = 915*percentByFile
                // if(event.getPercent()){
                //     this.mLoadBfb.string = Math.floor(event.getPercentByFile()*100)+'%';
                // }
                // this.mLoadBfb.string = Math.floor(percentByFile*100)+'%';

                // var msg = event.getMessage();
                // if (msg) {
                //     // this.panel.info.string = 'Updated file: ' + msg;
                //     cc.log(event.getPercent()/100 + '% : ' + msg);
                // }
                
                break;
            case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
            case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
                cc.log('Fail to download manifest file, hot update skipped.');
                failed = true;
                break;
            case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
                cc.log('Already up to date with the latest remote version.');
                failed = true;
                break;
            case jsb.EventAssetsManager.UPDATE_FINISHED:
                this.pushLog('Update finished. ' + event.getMessage());
                needRestart = true;
                break;
            case jsb.EventAssetsManager.UPDATE_FAILED:
                this.pushLog('Update failed. ' + event.getMessage());
                // this.mInfo.string = '更新失败，是否重试?';
                this._updating = false;
                this._canRetry = true;
                this.retry();
                // this.updateBtn.active = true;
                // needRestart = true;
                // this.hotUpdate();
                break;
            case jsb.EventAssetsManager.ERROR_UPDATING:
                this.pushLog('Asset update error: ' + event.getAssetId() + ', ' + event.getMessage());
                // needRestart = true;
                // this.hotUpdate();
                break;
            case jsb.EventAssetsManager.ERROR_DECOMPRESS:
                cc.log(event.getMessage());
                needRestart = true;
                break;
            default:
                break;
        }

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

        if (needRestart) {
            this._am.setEventCallback(null);
            // Prepend the manifest's search path
            var searchPaths = jsb.fileUtils.getSearchPaths();
            var newPaths = this._am.getLocalManifest().getSearchPaths();
            cc.log(JSON.stringify(newPaths));
            Array.prototype.unshift.apply(searchPaths, newPaths);
            // 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();
            setTimeout(() => {
                cc.game.restart();
            }, 1000)
            
        }
    }

    // public loadCustomManifest() {
    //     if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
    //         var manifest = new jsb.Manifest(customManifestStr, this._storagePath);
    //         this._am.loadLocalManifest(manifest, this._storagePath);
    //         this.panel.info.string = 'Using custom manifest';
    //     }
    // }
    
    public retry(){
        if (!this._updating && this._canRetry) {
            this._canRetry = false;
            
            // cc.log('Retry failed Assets...');
            this.pushLog('Retry failed Assets...');
            this._am.downloadFailedAssets();
        }
    }
    public onDestroy () {
        if (this._am) {
            this._am.setEventCallback(null);
        }
    }

    public pushLog(msg){
        // let node = new cc.Node();
        // node.anchorX = 0;
        // node.height = 32;
        // node.color = cc.color(0,0,0);
        // let log = node.addComponent(cc.Label)
        // log.overflow = cc.Label.Overflow.RESIZE_HEIGHT;
        // log.string = msg;
        // log.node.parent = this.mLogContent;
    }

    public onclickCancel(){
        this.checkUpdate();
    }

}
