﻿using System.Collections;
using JLGames.RocketDriver.Actions.Utils;
using JLGames.RocketDriver.CSharp.Utils;
using UnityEngine;
using UnityEngine.Networking;

namespace JLGames.RocketDriver.Actions.Loaderx
{
    internal class AbBundleLoader : IBundleLoader
    {
        private readonly LoaderBaseInfo m_BaseInfo;
        private LoaderVersion m_Version;
        private readonly AbManifestLoading m_VersionLoading;
        private readonly BundleRefSet m_AbSet;

        public bool UnderVersionCache => null != m_Version;

        public LoaderVersion BundleVersion => m_Version;

        internal AbBundleLoader(LoaderBaseInfo baseInfo)
        {
            m_BaseInfo = baseInfo;
            m_VersionLoading = new AbManifestLoading(LoadingAsyncType.AssetBundleManifest, null);
            m_AbSet = new BundleRefSet();
        }

        public IEnumerator InitVersion(LoaderDelegate.OnAssetLoaded<AssetBundleManifest> onVersionAssetBundleLoaded)
        {
            var isLoading = m_VersionLoading.IsLoading;
            m_VersionLoading.AddAction(onVersionAssetBundleLoaded);
            if (isLoading) yield break;

            m_VersionLoading.StartLoading();
            var versionPath = UnityPathUtil.CombineUnityPath(m_BaseInfo.BaseReqUri, $"{LoaderDefine.PlatformName}");
            DebugUtil.Log($"VersionPath={versionPath}");
            using (var req = UnityWebRequestAssetBundle.GetAssetBundle(versionPath))
            {
                yield return req.SendWebRequest();

#if UNITY_2020_1_OR_NEWER
                DebugUtil.Log($"InitVersion.Result:{req.result}");
                if (req.result == UnityWebRequest.Result.ConnectionError || req.result == UnityWebRequest.Result.ProtocolError)
                {
                    m_VersionLoading.Invoke(null, false);
                }
#else
                DebugUtil.Log($"InitVersion.Result:{req.isNetworkError},{req.isHttpError}");
                if (req.isNetworkError || req.isHttpError)
                {
                    m_VersionLoading.Invoke(null, false);
                }
#endif
                else
                {
                    var ab = DownloadHandlerAssetBundle.GetContent(req);
                    DebugUtil.Log($"InitVersion.AssetBundle:{ab}");
                    if (null == ab)
                    {
                        m_VersionLoading.Invoke(null, false);
                        yield break;
                    }

                    var manifest = ab.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
                    DebugUtil.Log($"InitVersion.VersionManifest:{manifest}");
                    if (null == manifest)
                    {
                        m_VersionLoading.Invoke(null, false);
                        yield break;
                    }

                    m_Version = new LoaderVersion(manifest);
                    CacheInstance.CacheManager.AddCache(m_BaseInfo.CacheName, m_BaseInfo.CachePath);
                    CacheInstance.CacheManager.GotoCache(m_BaseInfo.CacheName);
                    m_VersionLoading.Invoke(manifest, true);
                    ab.Unload(false);
                }
            }
        }

        //Bundle加载------------------------------------------------------------

        public IEnumerator LoadBundleAsync(string bundleName, LoaderDelegate.OnBundleLoaded onBundleLoaded,
            bool autoRelease = true, bool unloadObjects = false)
        {
            if (string.IsNullOrEmpty(bundleName))
            {
                throw LoaderErrors.ErrorNameEmpty;
            }

            var abRef = m_AbSet.GetBundleRef(bundleName,
                AssetBundleStatus.Init | AssetBundleStatus.Loading | AssetBundleStatus.Loaded);
            if (null != abRef)
            {
                abRef.UpgradeReleaseStatus(autoRelease, unloadObjects);
                abRef.AddAction(onBundleLoaded);
                yield break;
            }

            abRef = new BundleRef(bundleName, autoRelease, unloadObjects);
            abRef.AddAction(onBundleLoaded);
            abRef.StartLoading();
            m_AbSet.AddRef(abRef);
            using (var req = InnerCreateUnityBundleRequest(bundleName))
            {
                yield return req.WebRequest.SendWebRequest();
#if UNITY_2020_1_OR_NEWER
                if (req.WebRequest.result == UnityWebRequest.Result.ConnectionError
                    || req.WebRequest.result == UnityWebRequest.Result.ProtocolError)
#else
                if (req.WebRequest.isNetworkError || req.WebRequest.isHttpError)
#endif
                {
                    abRef.FinishLoading(null);
                }
                else
                {
//                    Debug.Log($"LoadBundleAsync({bundleName}) Succ!");
                    var ab = DownloadHandlerAssetBundle.GetContent(req.WebRequest);
                    abRef.FinishLoading(ab);
                    InnerTryClearOverVersions(req);
                }
            }
        }

        public IEnumerator LoadBundleWithDependenciesAsync(string bundleName, LoaderDelegate.OnBundleLoaded onBundleLoaded,
            bool autoRelease = true, bool unloadObjects = false)
        {
            if (string.IsNullOrEmpty(bundleName))
            {
                throw LoaderErrors.ErrorNameEmpty;
            }

            if (!UnderVersionCache)
            {
                yield break;
            }

            var dep = m_Version.GetDependencies(bundleName);
            var names = ArrayUtil.MergeArray(dep, bundleName);

            if (names.Length == 1)
            {
                yield return LoadBundleAsync(names[0], onBundleLoaded, autoRelease, unloadObjects);
            }
            else
            {
                yield return LoadMultiBundleAsync(names, (abs, suc) =>
                {
                    var last = abs[abs.Length - 1];
                    if (!suc) Internal.ApplyBundleCallback(onBundleLoaded, last, false);
                    Internal.ApplyBundleCallback(onBundleLoaded, last, true);
                }, autoRelease, unloadObjects);
            }
        }

        public IEnumerator LoadMultiBundleAsync(string[] bundleNames, LoaderDelegate.OnMultiBundleLoaded onMultiAssetLoaded,
            bool autoRelease = true, bool unloadObjects = false)
        {
            if (null == bundleNames)
            {
                Internal.ApplyMultiBundleCallback(onMultiAssetLoaded, null, false);
                yield break;
            }

            var bundleRefs = new BundleRef[bundleNames.Length];
            for (var i = 0; i < bundleRefs.Length; i++)
            {
                var bundleName = bundleNames[i];
                var index = i;
                bool succ = false;
                yield return LoaderManager.Mono.StartCoroutine(LoadBundleAsync(bundleName, (abRef, suc) =>
                {
                    succ = suc;
                    if (suc)
                    {
                        bundleRefs[index] = abRef;
                        abRef.SetTempBundle();
                    }
                }, autoRelease, unloadObjects));

                if (!succ)
                {
                    Internal.ApplyMultiBundleCallback(onMultiAssetLoaded, bundleRefs, false);
                    goto Release;
                }
            }

            Internal.ApplyMultiBundleCallback(onMultiAssetLoaded, bundleRefs, true);

            Release:

            if (autoRelease)
            {
                for (var index = 0; index < bundleRefs.Length; index++)
                {
                    bundleRefs[index]?.ReleaseTemp();
                }
            }
        }

        //-----------------------------------------------

        private AssetBundleLoaderRequest InnerCreateUnityBundleRequest(string name)
        {
            var rs = new AssetBundleLoaderRequest {BundleName = name};
            var uri = UnityPathUtil.CombineUnityPath(m_BaseInfo.BaseReqUri, name);
            if (!UnderVersionCache)
            {
                rs.WebRequest = UnityWebRequestAssetBundle.GetAssetBundle(uri);
            }
            else
            {
                rs.Cached = m_Version.GetCachedInfo(name);
                rs.OnCaching = true;
                rs.WebRequest = UnityWebRequestAssetBundle.GetAssetBundle(uri, rs.Cached);
            }

            return rs;
        }

        private static void InnerTryClearOverVersions(AssetBundleLoaderRequest req)
        {
            if (req.OnCaching)
            {
                CacheInstance.CacheManager.ClearOverVersions(req.BundleName);
//                CacheInstance.CacheManager.ClearOverVersions(req.BundleName, req.Cached.hash);
            }
        }
    }
}