using System.Collections.Generic;
using System.IO;
using System.Threading;
using Thousandto.Update.Data;
using Thousandto.Update.Delegate;
using Thousandto.Update.Enum;
using Thousandto.Update.Flow;
using Thousandto.Update.Log;
using Thousandto.Update.Singleton;
using AppManager = UnityEngine.Gonbest.MagicCube.AppManager;


namespace Thousandto.Update.Manager
{
    /// <summary>
    /// 更新流程管理器
    /// 注:对应IOS有几点需要注意
    ///1. 当包内有分段资源,启动时不做资源转移
    ///2. 当BaseVersion=0时,强制转移资源
    ///3. 当有更新时,无论分段更新还是patch,都要先做转移资源
    ///    a)分段更新:在分段更新模块做资源转移
    ///    b)patch更新:在RemoteVersion模块做资源转移
    /// </summary>
    public partial class UpdateManager : Singleton<UpdateManager>
    {
        //测试下载异常
        public static bool TestDownloadException = false;
        //保存下载文件的日志
        public static bool SAVA_DOWNLOAD_FILE_LOG = true;

        public static int CPUCORES = 4;

        private PlatformType _platformType;
        private string _localXmlPath;
        private string _storePath;
        private string _appPath;

        #region //预加载资源,显示进度用的
        //开启预加载,显示进度用
        private bool _showPreloadRes;
        //总共需要预加载的资源数
        private int _totalPreloadRes;
        //已经加载好的资源数
        private int _loadedRes;
        #endregion

        //是否初始化
        private bool _initialized;
        //流程是否结束,仅用来判断线程结束
        private bool _threadFinish = true;

        //重新开始,为true则在所有流程结束的时候重新开启整个流程
        private bool _restart;

        private bool _enableObb = false;
        //流程列表
        private List<BaseFlow> _flowList;
        //流程结束调用
        private FinishCallback _onFinish;
        //中断流程
        private bool _abortFlows;

        //场景资源列表配置文件路径
        private string _sceneConfigPath;
        public string StorePath { get { return _storePath; } }
        public BaseFlow CurrentFlow;

        //包体内是否存在FileList.txt文件
        public bool IsExistFileList { get; private set; }


        /// <summary>
        /// 初始化,重复调用只生效一次
        /// </summary>
        /// <param name="storedLocalXmlPath">存储在指定资源存放位置的LocalVersion.xml的绝对路径</param>
        /// <param name="inAppLocalXmlPath">存储在app包里面的LocalVersion.xml的绝对路径</param>
        /// <param name="storePath">指定存储资源的根路径</param>
        /// <param name="platformType">平台类型:Android/IOS/Windows</param>
        /// <param name="halfCpuCoreCount">CPU核心数的一半作为线程个数</param>
        public void Initialize(string storedLocalXmlPath, string inAppLocalXmlPath, string appPath, string storePath, PlatformType ptype, bool enableObb = false, int halfCpuCoreCount = 4)
        {
            CPUCORES = halfCpuCoreCount;
            if (this._initialized)
            {
                UpdateLog.WARN_LOG("_initialized: " + true);
            }
            else
            {
                UpdateLog.DEBUG_LOG(string.Format("Update Initialize: storedLocalXmlPath = {0}\n inAppLocalXmlPath ={1}\n appPath={2}\n storePath={3}\n needTransResource={4}", storedLocalXmlPath, inAppLocalXmlPath, appPath, storePath, (ptype== PlatformType.Android)));
                this._threadFinish = true;
                this._localXmlPath = storedLocalXmlPath;
                this._storePath = storePath;
                this._appPath = appPath;
                _enableObb = enableObb;
                this._platformType = ptype;
                this._flowList = new List<BaseFlow>();
                if (_enableObb)
                {
                    this._flowList.Add(new Flow1TransResourceExOBB());
                }
                this._flowList.Add(new Flow1TransResource());
                this._flowList.Add(new Flow2LocalXml());
                this._flowList.Add(new Flow3RemoteXml());
                this._flowList.Add(new Flow7ExDownloadMapFile());
                this._flowList.Add(new Flow8ExCheckResource());
                this._flowList.Add(new Flow9RepairResource());
                this._flowList.Add(new FlowFinish());
                this._initialized = true;
                BaseFlow.SetPath(storedLocalXmlPath, inAppLocalXmlPath, storePath, appPath);

                InitBackDownload(halfCpuCoreCount);               

                Recorder.StepRecorder.Initialize();
            }

        }

        public void Unitialize()
        {
            AbortFlows();
            _initialized = false;
            if (_flowList != null)
            {
                _flowList.Clear();
            }
            _onFinish = null;
        }

        /// <summary>
        /// 日志回调注册
        /// </summary>
        /// <param name="log1"></param>
        /// <param name="log2"></param>
        /// <param name="log3"></param>
        public void RegisterLog(DefaultLog log1, WarnLog log2, ErrorLog log3)
        {
            UpdateLog.RegisterLogCallback(log1, log2, log3);
        }

        /// <summary>
        /// 测试流程要用到的数据
        /// </summary>
        /// <param name="imeiOrMacOrIdfa">imei、mac地址、idfa</param>
        /// <param name="ip">ip地址,基本没用了</param>
        public void SetImeiOrMacOrIdfa(string imeiOrMacOrIdfa)
        {
            UpdateLog.WARN_LOG("UpdateManager ---> SetImeiOrMacOrIdfa() ");
            if (this._initialized)
            {
                this.FlowInstance<Flow3RemoteXml>().SetExternalData(imeiOrMacOrIdfa, "");
            }
        }

        /// <summary>
        /// 每个流程开始时调用,用来记录流程进程
        /// </summary>
        /// <param name="callback"></param>
        public void SetOnFlowBeginCallback(ActionCall callback)
        {
            if (!_initialized) return;
            convertActionCall(callback);
            BaseFlow.SetPerFlowActionCallback(onActionCall);
        }

        /// <summary>
        /// 设置转移资源是要用到的数据
        /// </summary>
        /// <param name="inAppClientVersion">包内app版本</param>
        /// <param name="inAppBaseVersion">包内分段版本</param>
        /// <param name="appPath">app安装路径,ios是var目录,apk是/data/data目录</param>
        public void SetTransData(string inAppClientVersion, string inAppBaseVersion, string appPath, TransResourceFinishCallback callback)
        {
            UpdateLog.DEBUG_LOG(string.Format("SetTransData: inAppClientVersion={0} inAppBaseVersion={1} appPath={2}", 
                inAppClientVersion, inAppBaseVersion, appPath));
            if (!_initialized) return;
            BaseFlow.SetInAppVer(inAppClientVersion, inAppBaseVersion);
            convertFuncTransResourceFinishCallback(callback);
            FlowInstance<Flow1TransResource>().SetExternalData(appPath, _platformType, onTransResourceFinishCallback);

            if (_enableObb)
                FlowInstance<Flow1TransResourceExOBB>().SetExternalData(_storePath, _platformType, onTransResourceFinishCallback);

        }

        /// <summary>
        /// 设置备份的cdn列表,当前cdn下载失败后,自动转到下一个cdn下载
        /// </summary>
        /// <param name="backupCdnArray"></param>
        public void SetBackupCdn(string[] backupCdnArray)
        {
            //Thousandto.Update.Download.Download.BackupCdn = backupCdnArray;
        }

        /// <summary>
        /// 设置场景文件关联的资源列表的配置文件路径:SceneConfig
        /// </summary>
        /// <param name="path"></param>
        public void SetSceneReferenceResConfigPath(string dir)
        {
            _sceneConfigPath = dir;
        }

        /// <summary>
        /// 客户端下载完成后的回调:安卓是直接安装,IOS是打开网页
        /// 自定义下载客户端的方法,ios不下载,windows不下载,一般设为null
        /// </summary>
        /// <param name="func1"></param>
        public void SetClientDownClientFunc(ClientDownloadFinishCallback onFinish, CustomDownClientFunc customFunc = null)
        {
            //if (!_initialized) return;
            //convertFuncClientDownloadFinishCallback(onFinish);
            //FlowInstance<Flow4DownloadClient>().SetExternalData(onClientDownloadFinishCallback, customFunc, _platformType == PlatformType.IOS);
        }

        /// <summary>
        /// 有资源需要下载时,要提示给用户,这个函数是通知弹出提示框用的
        /// </summary>
        /// <param name="func"></param>
        public void SetDownloadNoticeFunc(DownloadNoticeCall func)
        {
            if (!_initialized) return;
            convertFuncDownloadNoticeCall(func);
            BaseFlow.SetDownloadNoticeCallback(onDownloadNoticeCall);
        }

        /// <summary>
        /// 流程结束的回调
        /// </summary>
        /// <param name="func"></param>
        public void SetFinishCallback(FinishCallback func)
        {
            if (!_initialized) return;
            convertFuncFinishCallback(func);
            _onFinish = onFinishCallback;
            FlowInstance<FlowFinish>().SetExternalData(onFinishCallback);
        }
        
        /// <summary>
        /// 语言
        /// </summary>
        /// <returns></returns>
        public string GetLanguage()
        {
            return FlowInstance<Flow2LocalXml>().LocalXml.Developer;
        }

        /// <summary>
        /// 获取一个服务器地址,这个地址用于上传日志信息
        /// </summary>
        /// <returns></returns>
        public string GetUploadServerURL()
        {
            return FlowInstance<Flow2LocalXml>().LocalXml.UploadServerURL;
        }

        /// <summary>
        /// 获取安装目录:apk路径/ios的Raw目录
        /// </summary>
        /// <returns></returns>
        public string GetInstallPath()
        {
            return _appPath;
        }

        /// <summary>
        /// 设置预加载信息,用来显示进度
        /// </summary>
        /// <param name="total"></param>
        /// <param name="loadedCount"></param>
        public void SetPreloadTotal(int total)
        {
            UpdateLog.WARN_LOG("start preload+++++++++++++");
            _showPreloadRes = true;
            _totalPreloadRes = total;
        }

        /// <summary>
        /// 已经预加载好的数量
        /// </summary>
        /// <param name="loaded"></param>
        public void SetPreloadedCount(int loaded)
        {
            _loadedRes = loaded;
        }

        /// <summary>
        /// 获取预加载进度信息,在UIUpdateForm使用
        /// </summary>
        /// <param name="total"></param>
        /// <param name="loaded"></param>
        /// <returns></returns>
        public bool ShowPreloadPregress()
        {
            return _showPreloadRes;
        }

        public int GetPreloadTotal()
        {
            return _totalPreloadRes;
        }

        public int GetPreloadedCount()
        {
            return _loadedRes;
        }

        public PlatformType GetPlatformType()
        {
            return _platformType;
        }

        /// <summary>
        /// 获取本地版本信息
        /// </summary>
        /// <param name="appVer">本地app版本</param>
        /// <param name="resVer">本地资源版本</param>
        public void GetVersionInfo(out string appVer, out string resVer)
        {
            appVer = "";
            resVer = "";
            if (CurrentFlow != null && CurrentFlow.LocalXml != null)
            {
                appVer = CurrentFlow.LocalXml.LocalAppVersion;
                resVer = CurrentFlow.LocalXml.PatchResVersion;
            }
        }

      

        /// <summary>
        /// 远端最新app版本
        /// </summary>
        /// <returns></returns>
        public string GetRemoteAppVersion()
        {
            if (CurrentFlow != null && CurrentFlow.CurrentRemoteData != null)
            {
                return CurrentFlow.CurrentRemoteData.AppVersion;
            }
            return string.Empty;
        }

        /// <summary>
        /// 远端最新资源版本
        /// </summary>
        /// <returns></returns>
        public string GetRemoteResVersion()
        {
            if (CurrentFlow != null && CurrentFlow.CurrentRemoteData != null)
            {
                return CurrentFlow.CurrentRemoteData.PatchVersion;
            }
            return string.Empty;
        }

        /// <summary>
        /// 更新流程是否正在运行
        /// </summary>
        /// <returns></returns>
        public bool Running()
        {
            return !_threadFinish;
        }


        /// <summary>
        /// 中断更新流程
        /// </summary>
        public void AbortFlows()
        {
            _abortFlows = true;
            if (CurrentFlow != null)
            {
                CurrentFlow.Abort();
                UpdateLog.WARN_LOG("中断流程 " + CurrentFlow.FlowName());
            }

            Thousandto.Update.Download.BackDownload.AbortAll(null);
        }

        public T FlowInstance<T>() where T : BaseFlow
        {
            if (_flowList != null && _flowList.Count > 0)
            {
                for (int i = 0; i < _flowList.Count; ++i)
                {
                    if (_flowList[i] is T)
                    {
                        return (T)_flowList[i];
                    }
                }
            }
            return default(T);
        }


        /// <summary>
        /// 开始走更新流程
        /// </summary>
        public void StartUpdate()
        {
            //未初始化
            if (!_initialized) return;
            //未完成
            if (!_threadFinish) return;

            //需要先调用一次,避免UI线程的函数在子线程中调用了
            Update();

            _threadFinish = false;
            _restart = false;
            Thread thread = new Thread(updateFlowByThread);
            thread.Start();
            thread.IsBackground = true;
        }

        /// <summary>
        /// 在中断所有流程后重新开始
        /// </summary>
        public void Restart()
        {
            if (_enableObb)
                FlowInstance<Flow1TransResourceExOBB>().ReInitTransData();

            //重置转移资源的数据
            var transResFlow = FlowInstance<Flow1TransResource>();
            if (transResFlow == null)
            {
                StartUpdate();
            }
            else
            {
                transResFlow.ReInitTransData();
                AbortFlows();
                if (Running())
                {
                    _restart = true;
                }
                else
                    StartUpdate();
            }
        }

        /// <summary>
        /// 是否已经转移过资源了,可以用此来判断资源读取路径是从app包里还是转移出去的资源路径
        /// </summary>
        /// <returns></returns>
        public bool HasTransedRes()
        {
            return BaseFlow.HasTransedResource();
        }

        /// <summary>
        /// 获取客户端下载地址,在Flow3后才能获取
        /// </summary>
        /// <returns></returns>
        public string GetClientUrl()
        {
            if (CurrentFlow != null && CurrentFlow.CurrentRemoteData != null)
                return CurrentFlow.CurrentRemoteData.ClientUrl;

            return "";
        }

        /// <summary>
        /// 返回apk存放位置,仅安卓平台可用
        /// </summary>
        /// <returns></returns>
        public string GetApkPath()
        {
            if (CurrentFlow != null)
                return CurrentFlow.ApkStorePath;
            return "";
        }

        /// <summary>
        /// 获取下载信息
        /// </summary>
        /// <param name="url"></param>
        /// <param name="total"></param>
        /// <param name="progressValue"></param>
        public void GetDownloadInfo(out string url, out int total, out int progressValue, out bool isDownloadProgress)
        {
            isDownloadProgress = CurrentFlow.UseDownload;
            CurrentFlow.GetCurDownInfo(out url, out total, out progressValue);
        }

        /// <summary>
        /// 流程类型
        /// </summary>
        /// <returns></returns>
        public FlowEnum GetCurFlowType()
        {
            var flowName = CurrentFlow.FlowName();
            if (System.Enum.IsDefined(typeof(FlowEnum), flowName))
            {
                return (FlowEnum)System.Enum.Parse(typeof(FlowEnum), flowName);
            }
            return CurrentFlow.FlowType;
        }

        public bool TryGetInAppData(string relativePath, out string md5, out int size)
        {
            md5 = null;
            size = 0;
            var info = AppManager.Instance.GetAssetInfo(relativePath);
            if (info != null)
            {
                md5 = info.MD5;
                size = info.Size;
                return true;
            }            
            return false;
        }

        //删除之前释放出去的资源
        internal void DelelteReleasedFiles()
        {
            RepairRecordFileData recordData = new RepairRecordFileData();
            recordData.Read();
            var e = AppManager.Instance.AssetData.GetEnumerator();
            try
            {
                while (e.MoveNext())
                {
                    var savePath = _storePath + e.Current.Key;
                    if (File.Exists(savePath))
                    {
                        File.Delete(savePath);
                        recordData.IncRepairCount(e.Current.Key);
                    }
                }
            }
            finally
            {
                e.Dispose();
            }
            recordData.Write();
        }

      

        /// <summary>
        /// 在线程中执行更新流程
        /// </summary>
        private void updateFlowByThread()
        {
            bool continueFlow = false;
            CurrentFlow = null;
            _abortFlows = false;
            int resultCode = CodeDefine.RET_SUCCESS;
            int runFlowCount = 0;
            while (true && !_abortFlows)
            {
                BaseFlow oldFlow = null;
                continueFlow = false;
                runFlowCount = 0;
                for (int i = 0; !_abortFlows && i < _flowList.Count; ++i)
                {
                    runFlowCount++;
                    CurrentFlow = _flowList[i];
                    UpdateLog.DEBUG_LOG(CurrentFlow.FlowName());

                    CurrentFlow.OnEnter(oldFlow);
                    resultCode = CurrentFlow.Work();
                    CurrentFlow.OnLeave(resultCode);

                    //更新客户端,跳过所有流程
                    if (resultCode == CodeDefine.RET_SKIP_BY_DOWNLOAD_APP)
                    {
                        UpdateLog.DEBUG_LOG("Download Client finish, skip all left flows!!!");
                        break;
                    }

                    //需要强制释放资源,重新走更新流程
                    if (resultCode == CodeDefine.RET_SKIP_BY_FORCE_TRANS_RESOURCE)
                    {
                        FlowInstance<Flow1TransResource>().SetForceUnzip();
                        continueFlow = true;
                        break;
                    }

                    //中断操作
                    if (resultCode == CodeDefine.RET_SKIP_BY_ABORT)
                    {
                        UpdateLog.DEBUG_LOG("Abort flow -> " + CurrentFlow.FlowName());
                        break;
                    }

                    if (resultCode == CodeDefine.RET_SKIP_BY_DISABLEDOWNLOAD)
                    {
                        UpdateLog.DEBUG_LOG("Not support download, skip all flows!!!");
                        break;
                    }

                    //取消操作
                    if (resultCode == CodeDefine.RET_SKIP_BY_CANCEL)
                    {
                        UpdateLog.DEBUG_LOG("Skip flow by cancel download option, exit game!!!");
                        break;
                    }

                    if (resultCode < CodeDefine.RET_SUCCESS)
                    {
                        break;
                    }

                    oldFlow = CurrentFlow;
                }

                if (!continueFlow)
                    break;
            }

            if (runFlowCount != _flowList.Count)
            {
                FlowInstance<FlowFinish>().FinishWithError(resultCode);
            }

            _threadFinish = true;

            //重新开启
            if (_restart)
            {
                UpdateLog.DEBUG_LOG("Restart update");
                StartUpdate();
            }
            else
                UpdateLog.DEBUG_LOG("Finish update flow!!! " + resultCode);
        }
    }
}