
import { _decorator, Component, Node, director, ProgressBar, Sprite, view, UITransform, game, Asset, loader, systemEvent, sys } from 'cc';
import Network from '../module/network';
import { Util } from '../module/util';
import prefabManager from './manager/prefabManager';
const { ccclass, property } = _decorator;

@ccclass('Update')
export class Update extends Component {
    @property(Node)
    bg: Node = null!;
    @property(Node)
    bar: Node = null!;
    @property(Node)
    worm: Node = null!;
    @property(ProgressBar)
    fileProgress: ProgressBar = null!;
    @property(Asset)
    manifestUrl: Asset = null!;
    private _storagePath ='';
    private _updating = false;
    private _canRetry = false;
    private _am: jsb.AssetsManager = null!;
    private _failCount = 0;
    private versionCompareHandle: (versionA: string, versionB: string) => number = null!;
    private preManager: prefabManager = null!;
    private _version_url = 'http://qkl.zamentui.com/version.php';
    private _onlineVersion:any|null = null!; // 远程版本信息
    private _localManifestPath = ''; // 本地更新文件路径
    

    start () {
        // 非原生系统直接跳过更新
        if (typeof jsb == "undefined") {
            director.loadScene("main");
            return;
        }
        window.GlobalEvent.Init(); // 初始化全局监听

        systemEvent.on("showMsg", this.showMsg, this);


        if(!this.preManager)
            this.preManager = window.PrefabManager;
        this._storagePath = this.getRootPath();
        Util.log('Storage path for remote asset : ' + this._storagePath);
        // 本地版本废弃则不执行更新,等待重启
        if(false == this.checkLocalVersion()){
            return;
        }
        

        this.versionCompareHandle = function (versionA: string, versionB: string) {
            Util.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;
            }
        };

        // 创建热更管理器
        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: string, asset: any) {
            // 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) {
                Util.log("Verification passed : " + relativePath);
                return true;
            }
            else {
                Util.log("Verification passed : " + relativePath + ' (' + expectedMD5 + ')');
                return true;
            }
        });

        this.changeProgress(0);
        this.checkVersion();
    }
    onDestroy(){
        systemEvent.off("showMsg", this.showMsg, this);
        Util.log('更新场景已销毁...');
    }
    onEnable(){
        let WinSize = view.getVisibleSize();
        // 背景铺满屏幕
        if(this.bg){
            let u = this.bg.getComponent(UITransform);
            if(u){
                let bei = WinSize.x / u.width;
                let bei2 = WinSize.y / u.height;
                if(bei2 > bei){
                    bei = bei2;
                }
                this.bg.setScale(bei,bei);
            }
        }
    }
    // 检查本地版本文件是否是最新的,如果比保存的版本更高则代表是覆盖安装了最新包,需要删除更新的资源后重启
    checkLocalVersion(){
        let LocalManifest = jsb.fileUtils.getStringFromFile(this.manifestUrl.nativeUrl);
        let LocalManifestObject = JSON.parse(LocalManifest);
        let LocalSaveVersion = sys.localStorage.getItem('mainVersion');
        LocalSaveVersion = LocalSaveVersion ? parseInt(LocalSaveVersion) : 1;

        Util.log('manifestVersion = ' + LocalManifestObject.version + ' saveVersion = ' + LocalSaveVersion);

        let v = LocalManifestObject.version.split('.');
        let manifestVersion = v[2] ? parseInt(v[2]) : 1;

        // 本地未保存版本号则保存版本号
        if(!sys.localStorage.getItem('mainVersion')){
            sys.localStorage.setItem('mainVersion', manifestVersion);
        }
        // 本地版本大于保存的版本,且缓存目录存在则更新本地保存的版本,并删除资源文件夹,重启app
        if(manifestVersion > LocalSaveVersion && jsb.fileUtils.isDirectoryExist(this._storagePath)){
            Util.log('删除本地资源目录.重启应用..');
            sys.localStorage.setItem('mainVersion', manifestVersion);
            jsb.fileUtils.removeDirectory(this._storagePath);
            this.changeProgress(1);
            setTimeout(() => {
                game.restart();
            }, 1000)
            return false;
        }
        return true;
    }
    // 获取服务器版本列表信息
    checkVersion(){
        let pThis = this;
        Network.getOnlineVersion(this._version_url, function(type:number, data:string) {
            Util.log('type=='+type.toString()+'  data = '+ data);
            if(type == 1 && data && JSON.parse(data)){
                let Online = JSON.parse(data);
                let Local = sys.localStorage.getItem('mainVersion');
                Local = Local ? parseInt(Local) : 1;
                Util.log('本地版本:'+Local);

                // 远程版本更高则更新链接地址,执行检查更新
                if(Online.mainVersion > Local){
                    pThis._onlineVersion = Online;
                    // 如果本地版本小于强制更新版本则弹框更新
                    if(Local < Online.needDownNewAppVersion){
                        // 如果当前版本小于强制更新版本的最低版本则一定需要更新
                        if(Local < Online.minimumNeedDownVersion){
                            pThis.showMsgBox('提示', '发现新版本,请您下载最新版本体验.', false, function(){
                                sys.openURL(Online.downUrl);
                            }.bind(pThis));
                        }
                        // 否则可以暂时不更新
                        else{
                            pThis.showMsgBox('提示', '发现新版本,请您下载最新版本体验.', true, 
                            function(){
                                sys.openURL(Online.downUrl);
                            }.bind(pThis),
                            function(){
                                Util.log('取消强制更新...');
                                pThis.changeProgress(1);
                                setTimeout(() => {
                                    director.loadScene("main");
                                }, 1000)
                            }.bind(pThis));
                        }
                    }
                    else{
                        let path = pThis.manifestUrl.nativeUrl;
                        pThis._localManifestPath = pThis.modifyAppLoadUrlForManifestFile(path);
                        pThis.checkUpdate(); // 开始检查更新
                    }
                }
                else{
                    Util.log('当前版本:'+Local+' 远程版本:'+Online.mainVersion+'  无需更新...');
                    pThis.changeProgress(1);
                    setTimeout(() => {
                        director.loadScene("main");
                    }, 1000)
                }
            }
            else{
                pThis.showMsgBox('提示', '获取版本信息失败,请重试.', false, pThis.checkVersion.bind(pThis));
            }
        }.bind(this))
    }
    // 对比远程文件版本
    checkCb(event: any) {
        Util.log('Code: ' + event.getEventCode());
        let isNeedUpdate = false; // 是否需要更新
        let isUpdateOk = false; // 是否最新版本
        switch (event.getEventCode()) {
            case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
                Util.log("No local manifest file found, hot update skipped.");
                break;
            case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
            case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
                Util.log("Fail to download manifest file, hot update skipped.");
                this.showMsgBox('提示', '更新失败,请检查网络后重试.', false, this.checkUpdate.bind(this));
                break;
            case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
                Util.log("Already up to date with the latest remote version.");
                isUpdateOk = true;
                break;
            case jsb.EventAssetsManager.NEW_VERSION_FOUND:
                // 这里检查到新版本且有需要更新的数据才执行更新
                if(this._am.getTotalBytes() > 0){
                    Util.log('New version found, please try to update. (' + Math.ceil(this._am.getTotalBytes() / 1024) + 'kb)');
                    this.changeProgress(0);
                    isNeedUpdate = true;
                }
                else{
                    Util.log('New version found, but need totalBytes is zero!');
                    isUpdateOk = true;
                }
                break;
            default:
                return;
        }


        this._am.setEventCallback(null!);
        this._updating = false;
        if(isNeedUpdate){
            this.hotUpdate();
        }
        // 最新版本则跳转场景
        if(isUpdateOk){
            this.changeProgress(1);
            setTimeout(() => {
                director.loadScene("main");
            }, 1000)
        }
    }

    updateCb(event: any) {
        var needRestart = false;
        var failed = false;
        switch (event.getEventCode()) {
            // 本地对比文件不存在
            case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
                Util.log('No local manifest file found, hot update skipped.');
                failed = true;
                break;
                // 更新进度通知
            case jsb.EventAssetsManager.UPDATE_PROGRESSION:
               // this.panel.byteProgress.progress = event.getPercent(); // 下载进度
                this.changeProgress(event.getPercentByFile()); // 文件下载进度

                Util.log('文件进度:' + event.getDownloadedFiles() + ' / ' + event.getTotalFiles());
                Util.log('字节进度:' + event.getDownloadedBytes() + ' / ' + event.getTotalBytes());
                var msg = event.getMessage();
                if (msg) {
                    //this.panel.info.string = 'Updated file: ' + msg;
                    Util.log(event.getPercent()/100 + '% : ' + msg);
                }
                break;
                // 远程对比文件下载失败
            case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
            case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
                Util.log('Fail to download manifest file, hot update skipped.');
                this._updating = false;
                this._canRetry = true;
                this.showMsgBox('提示', '更新失败,请检查网络后重试..', false, this.retry.bind(this));
                break;
                // 已是最新版本
            case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
                Util.log('Already up to date with the latest remote version.');
                failed = true;
                this.changeProgress(1);
                setTimeout(() => {
                    director.loadScene("main");
                }, 1000);
                break;
                // 更新完成
            case jsb.EventAssetsManager.UPDATE_FINISHED:
                Util.log('Update finished. ' + event.getMessage());
                needRestart = true;
                break;
                // 更新失败
            case jsb.EventAssetsManager.UPDATE_FAILED:
                Util.log('Update failed. ' + event.getMessage());
                this._updating = false;
                this._canRetry = true;
                this.showMsgBox('提示', '更新失败,请检查网络后重试..', false, this.retry.bind(this));
                break;
                // 更新错误
            case jsb.EventAssetsManager.ERROR_UPDATING:
                Util.log('Asset update error: ' + event.getAssetId() + ', ' + event.getMessage());
                this._updating = false;
                this._canRetry = true;
                this.showMsgBox('提示', '更新失败,请检查网络后重试...', false, this.retry.bind(this));
                break;
                // 解压失败
            case jsb.EventAssetsManager.ERROR_DECOMPRESS:
                Util.log('ERROR_DECOMPRESS:' + event.getMessage());
                this._updating = false;
                this._canRetry = true;
                this.showMsgBox('提示', '更新失败,请检查网络后重试!', false, this.retry.bind(this));
                break;
            default:
                Util.log('default:' + event.getEventCode());
                break;
        }

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

        if (needRestart) {
            this._am.setEventCallback(null!);
            // Prepend the manifest's search path
            var searchPaths = jsb.fileUtils.getSearchPaths();
            var newPaths = this._am.getLocalManifest().getSearchPaths();
            Util.log('重启,newPaths==' + 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.
            localStorage.setItem('HotUpdateSearchPaths', JSON.stringify(searchPaths));
            jsb.fileUtils.setSearchPaths(searchPaths);
            // 保存版本号
            sys.localStorage.setItem('mainVersion', this._onlineVersion.mainVersion);

            // restart game.
            setTimeout(() => {
                game.restart();
            }, 1000)
        }
    }
    // 重新尝试更新
    retry() {
        if (!this._updating && this._canRetry) {
            this._canRetry = false;

            Util.log('Retry failed Assets...');
            this._am.downloadFailedAssets();
        }
    }
    // 检查更新
    checkUpdate() {
        if (this._updating) {
            Util.log('Checking or updating ...');
            return;
        }
        // 未初始化则初始化本地文件
        if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
            var url = this._localManifestPath ? this._localManifestPath : this.manifestUrl.nativeUrl;
            this._am.loadLocalManifest(url);
            Util.log('本地更新文件路径:'+ url);
        }
        if (!this._am.getLocalManifest() || !this._am.getLocalManifest().isLoaded()) {
            Util.log('Failed to load local manifest ...');
            return;
        }
        this._am.setEventCallback(this.checkCb.bind(this));

        this._am.checkUpdate();
        this._updating = true;
        let local = this._am.getLocalManifest();
        Util.log('本地banben:'+ local.getVersion());
    }
    // 开始热更
    hotUpdate() {
        if (this._am && !this._updating) {
            this._am.setEventCallback(this.updateCb.bind(this));

            if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
                var url = this._localManifestPath ? this._localManifestPath : this.manifestUrl.nativeUrl;
                this._am.loadLocalManifest(url);
            }
            Util.log('更新开始....');
            this._failCount = 0;
            this._am.update();
            this._updating = true;
        }
    }
    // 显示选择框
    showMsgBox(title:string, txt:string, isShowCannel:boolean=false, callback:Function|null=null, callback2:Function|null=null){
        if(this.preManager){
            this.preManager.msgBox(title, txt, isShowCannel, callback, callback2);
        }
    }
    modifyAppLoadUrlForManifestFile(localManifestPath:string) {
        Util.log("StoragePath for remote asset : " + this._storagePath);
        if (jsb.fileUtils.isFileExist(this.getRootPath() + '/project.manifest')) {
              Util.log("有下载的manifest文件");
              let loadManifest = jsb.fileUtils.getStringFromFile(this._storagePath + '/project.manifest');
              let manifestObject = JSON.parse(loadManifest);
              manifestObject.packageUrl = this._onlineVersion.packageUrl + this._onlineVersion.mainVersion + '/';
              manifestObject.remoteManifestUrl = manifestObject.packageUrl + 'project.manifest';
              manifestObject.remoteVersionUrl = manifestObject.packageUrl + 'version.manifest';
            
              let afterString = JSON.stringify(manifestObject);
              let isWritten = jsb.fileUtils.writeStringToFile(afterString, this._storagePath + '/project.manifest');
              Util.log("Written Status : "+isWritten);
        }else{
              if (!jsb.fileUtils.isDirectoryExist(this._storagePath)) jsb.fileUtils.createDirectory(this._storagePath);

              console.log("没有下载的manifest文件");
        
              //修改原始manifest文件
              let originManifestPath = localManifestPath;
              let originManifest = jsb.fileUtils.getStringFromFile(originManifestPath);
              let originManifestObject = JSON.parse(originManifest);
              originManifestObject.packageUrl = this._onlineVersion.packageUrl + this._onlineVersion.mainVersion + '/';
              originManifestObject.remoteManifestUrl = originManifestObject.packageUrl + 'project.manifest';
              originManifestObject.remoteVersionUrl = originManifestObject.packageUrl + 'version.manifest';
              let afterString = JSON.stringify(originManifestObject);
              let isWritten = jsb.fileUtils.writeStringToFile(afterString, this._storagePath + '/project.manifest');
          
              Util.log("Written Status : "+isWritten);
        }
        return this._storagePath + '/project.manifest';
    }
    getRootPath(){
        return ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'main_asset');
    }
    // 显示提示框
    showMsg(txt:string,timer:number = 0){
        if(this.preManager)
            this.preManager.msg(txt,timer);
    }
    // 更新进度条
    changeProgress(jindu:number){
        this.fileProgress.progress = jindu;
        let pthis = this;
        // 下一帧更新毛毛虫
        this.scheduleOnce(function () {
            let beginx = pthis.bar.getPosition().x-75;
            let end = pthis.bar.getComponent(UITransform)?.width;
            end = end ? end : 0;
            pthis.worm.setPosition(beginx+end, 0);
        }.bind(this), 0);
    }
    // update (deltaTime: number) {
    //     // [4]
    // }
}

