using System;
using System.Collections;
using System.Collections.Generic;

namespace YooAsset
{
    internal class PlayModeImpl : IPlayMode, IBundleQuery
    {
        public readonly string PackageName;
        public readonly EPlayMode PlayMode;
        public readonly List<IFileSystem> FileSystems = new List<IFileSystem>(10);

        public PlayModeImpl(string packageName, EPlayMode playMode)
        {
            PackageName = packageName;
            PlayMode = playMode;
        }

        /// <summary>
        /// 异步初始化
        /// </summary>
        public InitializationOperation InitializeAsync(FileSystemParameters fileSystemParameter)
        {
            var fileSystemParamList = new List<FileSystemParameters>();
            if (fileSystemParameter != null)
                fileSystemParamList.Add(fileSystemParameter);
            return InitializeAsync(fileSystemParamList);
        }

        /// <summary>
        /// 异步初始化
        /// </summary>
        public InitializationOperation InitializeAsync(FileSystemParameters fileSystemParameterA, FileSystemParameters fileSystemParameterB)
        {
            var fileSystemParamList = new List<FileSystemParameters>();
            if (fileSystemParameterA != null)
                fileSystemParamList.Add(fileSystemParameterA);
            if (fileSystemParameterB != null)
                fileSystemParamList.Add(fileSystemParameterB);
            return InitializeAsync(fileSystemParamList);
        }

        /// <summary>
        /// 异步初始化
        /// </summary>
        public InitializationOperation InitializeAsync(List<FileSystemParameters> fileSystemParameterList)
        {
            var operation = new InitializationOperation(this, fileSystemParameterList);
            return operation;
        }

        #region IPlayMode接口
        /// <summary>
        /// 当前激活的清单
        /// </summary>
        public PackageManifest ActiveManifest { set; get; }

        /// <summary>
        /// 销毁文件系统
        /// </summary>
        void IPlayMode.DestroyFileSystem()
        {
            foreach (var fileSystem in FileSystems)
            {
                fileSystem.OnDestroy();
            }
            FileSystems.Clear();
        }

        /// <summary>
        /// 向网络端请求最新的资源版本
        /// </summary>
        RequestPackageVersionOperation IPlayMode.RequestPackageVersionAsync(bool appendTimeTicks, int timeout)
        {
            var operation = new RequestPackageVersionImplOperation(this, appendTimeTicks, timeout);
            return operation;
        }

        /// <summary>
        /// 向网络端请求并更新清单
        /// </summary>
        UpdatePackageManifestOperation IPlayMode.UpdatePackageManifestAsync(string packageVersion, int timeout)
        {
            var operation = new UpdatePackageManifestOperation(this, packageVersion, timeout);
            return operation;
        }

        /// <summary>
        /// 预下载指定版本的包裹内容
        /// </summary>
        PreDownloadContentOperation IPlayMode.PreDownloadContentAsync(string packageVersion, int timeout)
        {
            var operation = new PreDownloadContentOperation(this, packageVersion, timeout);
            return operation;
        }

        /// <summary>
        /// 清理缓存文件
        /// </summary>
        ClearCacheFilesOperation IPlayMode.ClearCacheFilesAsync(ClearCacheFilesOptions options)
        {
            var operation = new ClearCacheFilesOperation(this, options);
            return operation;
        }

        // 下载相关
        ResourceDownloaderOperation IPlayMode.CreateResourceDownloaderByAll(int downloadingMaxNumber, int failedTryAgain, int timeout)
        {
            List<BundleInfo> downloadList = GetDownloadListByAll(ActiveManifest);
            var operation = new ResourceDownloaderOperation(PackageName, downloadList, downloadingMaxNumber, failedTryAgain, timeout);
            return operation;
        }
        ResourceDownloaderOperation IPlayMode.CreateResourceDownloaderByTags(string[] tags, int downloadingMaxNumber, int failedTryAgain, int timeout)
        {
            List<BundleInfo> downloadList = GetDownloadListByTags(ActiveManifest, tags);
            var operation = new ResourceDownloaderOperation(PackageName, downloadList, downloadingMaxNumber, failedTryAgain, timeout);
            return operation;
        }
        ResourceDownloaderOperation IPlayMode.CreateResourceDownloaderByPaths(AssetInfo[] assetInfos, bool recursiveDownload, int downloadingMaxNumber, int failedTryAgain, int timeout)
        {
            List<BundleInfo> downloadList = GetDownloadListByPaths(ActiveManifest, assetInfos, recursiveDownload);
            var operation = new ResourceDownloaderOperation(PackageName, downloadList, downloadingMaxNumber, failedTryAgain, timeout);
            return operation;
        }

        // 解压相关
        ResourceUnpackerOperation IPlayMode.CreateResourceUnpackerByAll(int upackingMaxNumber, int failedTryAgain, int timeout)
        {
            List<BundleInfo> unpcakList = GetUnpackListByAll(ActiveManifest);
            var operation = new ResourceUnpackerOperation(PackageName, unpcakList, upackingMaxNumber, failedTryAgain, timeout);
            return operation;
        }
        ResourceUnpackerOperation IPlayMode.CreateResourceUnpackerByTags(string[] tags, int upackingMaxNumber, int failedTryAgain, int timeout)
        {
            List<BundleInfo> unpcakList = GetUnpackListByTags(ActiveManifest, tags);
            var operation = new ResourceUnpackerOperation(PackageName, unpcakList, upackingMaxNumber, failedTryAgain, timeout);
            return operation;
        }

        // 导入相关
        ResourceImporterOperation IPlayMode.CreateResourceImporterByFilePaths(string[] filePaths, int importerMaxNumber, int failedTryAgain, int timeout)
        {
            List<BundleInfo> importerList = GetImporterListByFilePaths(ActiveManifest, filePaths);
            var operation = new ResourceImporterOperation(PackageName, importerList, importerMaxNumber, failedTryAgain, timeout);
            return operation;
        }
        #endregion

        #region IBundleQuery接口
        private BundleInfo CreateBundleInfo(PackageBundle packageBundle)
        {
            if (packageBundle == null)
                throw new Exception("Should never get here !");

            var fileSystem = GetBelongFileSystem(packageBundle);
            if (fileSystem != null)
            {
                BundleInfo bundleInfo = new BundleInfo(fileSystem, packageBundle);
                return bundleInfo;
            }

            throw new Exception($"Can not found belong file system : {packageBundle.BundleName}");
        }
        BundleInfo IBundleQuery.GetMainBundleInfo(AssetInfo assetInfo)
        {
            if (assetInfo == null || assetInfo.IsInvalid)
                throw new Exception("Should never get here !");

            // 注意：如果清单里未找到资源包会抛出异常！
            var packageBundle = ActiveManifest.GetMainPackageBundle(assetInfo.Asset);
            return CreateBundleInfo(packageBundle);
        }
        BundleInfo[] IBundleQuery.GetDependBundleInfos(AssetInfo assetInfo)
        {
            if (assetInfo == null || assetInfo.IsInvalid)
                throw new Exception("Should never get here !");

            // 注意：如果清单里未找到资源包会抛出异常！
            PackageBundle[] depends;
            if (assetInfo.LoadMethod == AssetInfo.ELoadMethod.LoadAllAssets)
            {
                var mainBundle = ActiveManifest.GetMainPackageBundle(assetInfo.Asset);
                depends = ActiveManifest.GetAllDependencies(mainBundle);
            }
            else
            {
                depends = ActiveManifest.GetAllDependencies(assetInfo.Asset);
            }

            List<BundleInfo> result = new List<BundleInfo>(depends.Length);
            foreach (var packageBundle in depends)
            {
                BundleInfo bundleInfo = CreateBundleInfo(packageBundle);
                result.Add(bundleInfo);
            }
            return result.ToArray();
        }
        string IBundleQuery.GetMainBundleName(int bundleID)
        {
            // 注意：如果清单里未找到资源包会抛出异常！
            var packageBundle = ActiveManifest.GetMainPackageBundle(bundleID);
            return packageBundle.BundleName;
        }
        string IBundleQuery.GetMainBundleName(AssetInfo assetInfo)
        {
            if (assetInfo == null || assetInfo.IsInvalid)
                throw new Exception("Should never get here !");

            // 注意：如果清单里未找到资源包会抛出异常！
            var packageBundle = ActiveManifest.GetMainPackageBundle(assetInfo.Asset);
            return packageBundle.BundleName;
        }
        string[] IBundleQuery.GetDependBundleNames(AssetInfo assetInfo)
        {
            if (assetInfo == null || assetInfo.IsInvalid)
                throw new Exception("Should never get here !");

            // 注意：如果清单里未找到资源包会抛出异常！
            var depends = ActiveManifest.GetAllDependencies(assetInfo.Asset);
            List<string> result = new List<string>(depends.Length);
            foreach (var packageBundle in depends)
            {
                result.Add(packageBundle.BundleName);
            }
            return result.ToArray();
        }
        #endregion

        /// <summary>
        /// 获取主文件系统
        ///  说明：文件系统列表里，最后一个属于主文件系统
        /// </summary>
        public IFileSystem GetMainFileSystem()
        {
            int count = FileSystems.Count;
            if (count == 0)
                return null;
            return FileSystems[count - 1];
        }

        /// <summary>
        /// 获取资源包所属文件系统
        /// </summary>
        public IFileSystem GetBelongFileSystem(PackageBundle packageBundle)
        {
            for (int i = 0; i < FileSystems.Count; i++)
            {
                IFileSystem fileSystem = FileSystems[i];
                if (fileSystem.Belong(packageBundle))
                {
                    return fileSystem;
                }
            }

            YooLogger.Error($"Can not found belong file system : {packageBundle.BundleName}");
            return null;
        }

        public List<BundleInfo> GetDownloadListByAll(PackageManifest manifest)
        {
            if (manifest == null)
                return new List<BundleInfo>();

            List<BundleInfo> result = new List<BundleInfo>(1000);
            foreach (var packageBundle in manifest.BundleList)
            {
                var fileSystem = GetBelongFileSystem(packageBundle);
                if (fileSystem == null)
                    continue;

                if (fileSystem.NeedDownload(packageBundle))
                {
                    var bundleInfo = new BundleInfo(fileSystem, packageBundle);
                    result.Add(bundleInfo);
                }
            }
            return result;
        }
        public List<BundleInfo> GetDownloadListByTags(PackageManifest manifest, string[] tags)
        {
            if (manifest == null)
                return new List<BundleInfo>();

            List<BundleInfo> result = new List<BundleInfo>(1000);
            foreach (var packageBundle in manifest.BundleList)
            {
                var fileSystem = GetBelongFileSystem(packageBundle);
                if (fileSystem == null)
                    continue;

                if (fileSystem.NeedDownload(packageBundle))
                {
                    // 如果未带任何标记，则统一下载
                    if (packageBundle.HasAnyTags() == false)
                    {
                        var bundleInfo = new BundleInfo(fileSystem, packageBundle);
                        result.Add(bundleInfo);
                    }
                    else
                    {
                        // 查询DLC资源
                        if (packageBundle.HasTag(tags))
                        {
                            var bundleInfo = new BundleInfo(fileSystem, packageBundle);
                            result.Add(bundleInfo);
                        }
                    }
                }
            }
            return result;
        }
        public List<BundleInfo> GetDownloadListByPaths(PackageManifest manifest, AssetInfo[] assetInfos, bool recursiveDownload)
        {
            if (manifest == null)
                return new List<BundleInfo>();

            // 获取资源对象的资源包和所有依赖资源包
            List<PackageBundle> checkList = new List<PackageBundle>();
            foreach (var assetInfo in assetInfos)
            {
                if (assetInfo.IsInvalid)
                {
                    YooLogger.Warning(assetInfo.Error);
                    continue;
                }

                // 注意：如果清单里未找到资源包会抛出异常！
                PackageBundle mainBundle = manifest.GetMainPackageBundle(assetInfo.Asset);
                if (checkList.Contains(mainBundle) == false)
                    checkList.Add(mainBundle);

                // 注意：如果清单里未找到资源包会抛出异常！
                PackageBundle[] mainDependBundles = manifest.GetAllDependencies(assetInfo.Asset);
                foreach (var dependBundle in mainDependBundles)
                {
                    if (checkList.Contains(dependBundle) == false)
                        checkList.Add(dependBundle);
                }

                // 下载主资源包内所有资源对象依赖的资源包
                if (recursiveDownload)
                {
                    foreach (var otherMainAsset in mainBundle.IncludeMainAssets)
                    {
                        var otherMainBundle = manifest.GetMainPackageBundle(otherMainAsset.BundleID);
                        if (checkList.Contains(otherMainBundle) == false)
                            checkList.Add(otherMainBundle);

                        PackageBundle[] otherDependBundles = manifest.GetAllDependencies(otherMainAsset);
                        foreach (var dependBundle in otherDependBundles)
                        {
                            if (checkList.Contains(dependBundle) == false)
                                checkList.Add(dependBundle);
                        }
                    }
                }
            }

            List<BundleInfo> result = new List<BundleInfo>(1000);
            foreach (var packageBundle in checkList)
            {
                var fileSystem = GetBelongFileSystem(packageBundle);
                if (fileSystem == null)
                    continue;

                if (fileSystem.NeedDownload(packageBundle))
                {
                    var bundleInfo = new BundleInfo(fileSystem, packageBundle);
                    result.Add(bundleInfo);
                }
            }
            return result;
        }
        public List<BundleInfo> GetUnpackListByAll(PackageManifest manifest)
        {
            if (manifest == null)
                return new List<BundleInfo>();

            List<BundleInfo> result = new List<BundleInfo>(1000);
            foreach (var packageBundle in manifest.BundleList)
            {
                var fileSystem = GetBelongFileSystem(packageBundle);
                if (fileSystem == null)
                    continue;

                if (fileSystem.NeedUnpack(packageBundle))
                {
                    var bundleInfo = new BundleInfo(fileSystem, packageBundle);
                    result.Add(bundleInfo);
                }
            }
            return result;
        }
        public List<BundleInfo> GetUnpackListByTags(PackageManifest manifest, string[] tags)
        {
            if (manifest == null)
                return new List<BundleInfo>();

            List<BundleInfo> result = new List<BundleInfo>(1000);
            foreach (var packageBundle in manifest.BundleList)
            {
                var fileSystem = GetBelongFileSystem(packageBundle);
                if (fileSystem == null)
                    continue;

                if (fileSystem.NeedUnpack(packageBundle))
                {
                    if (packageBundle.HasTag(tags))
                    {
                        var bundleInfo = new BundleInfo(fileSystem, packageBundle);
                        result.Add(bundleInfo);
                    }
                }
            }
            return result;
        }
        public List<BundleInfo> GetImporterListByFilePaths(PackageManifest manifest, string[] filePaths)
        {
            if (manifest == null)
                return new List<BundleInfo>();

            List<BundleInfo> result = new List<BundleInfo>();
            foreach (var filePath in filePaths)
            {
                string fileName = System.IO.Path.GetFileName(filePath);
                if (manifest.TryGetPackageBundleByFileName(fileName, out PackageBundle packageBundle))
                {
                    var fileSystem = GetBelongFileSystem(packageBundle);
                    if (fileSystem == null)
                        continue;

                    if (fileSystem.NeedImport(packageBundle))
                    {
                        var bundleInfo = new BundleInfo(fileSystem, packageBundle, filePath);
                        result.Add(bundleInfo);
                    }
                }
                else
                {
                    YooLogger.Warning($"Not found package bundle, importer file path : {filePath}");
                }
            }
            return result;
        }
    }
}