﻿using System;
using System.Collections.Generic;
using System.Text;
using JLGames.RocketDriver.CSharp.Utils;
using JLGames.RocketDriver.Actions.Utils;
using JLGames.RocketDriver.CSharp.Event;
using Object = UnityEngine.Object;

namespace JLGames.RocketDriver.Actions.Loaderx
{
    internal static class Internal
    {
        public static void ApplyBundleCallback(LoaderDelegate.OnBundleLoaded onBundleLoaded, BundleRef o, bool suc)
        {
            onBundleLoaded?.Invoke(o, suc);
        }

        public static void ApplyMultiBundleCallback(LoaderDelegate.OnMultiBundleLoaded onMultiBundleLoaded,
            BundleRef[] o, bool suc)
        {
            onMultiBundleLoaded?.Invoke(o, suc);
        }

        public static void ApplyAssetCallback<T>(LoaderDelegate.OnAssetLoaded<T> onAssetLoaded, T o, bool b)
            where T : Object
        {
            onAssetLoaded?.Invoke(o, b);
        }

        public static void ApplyMultiAssetCallback<T>(LoaderDelegate.OnMultiAssetLoaded<T> onMutliAssetLoaded, T[] o,
            bool suc) where T : Object
        {
            onMutliAssetLoaded?.Invoke(o, suc);
        }

        public static void ApplyAssetWithBundleCallback<T>(LoaderDelegate.OnAssetWithBundleLoaded<T> onLoaded,
            BundleRef ab, T o, bool suc) where T : Object
        {
            onLoaded?.Invoke(ab, o, suc);
        }

        public static Object FindAsset(Object[] assets, string assetName)
        {
            if (null == assets || assets.Length == 0) return null;
            for (var index = assets.Length - 1; index >= 0; index--)
            {
                if (assets[index].name == assetName) return assets[index];
            }

            return null;
        }

        public static Object FindAsset(Object[] assets, string assetName, Type type)
        {
            if (null == assets || assets.Length == 0) return null;
            for (var index = assets.Length - 1; index >= 0; index--)
            {
                if (assets[index].name != assetName) continue;
                if (assets[index].GetType() != type) continue;
                return assets[index];
            }

            return null;
        }

        public static T FindAsset<T>(Object[] assets, string assetName) where T : Object
        {
            if (null == assets || assets.Length == 0) return null;
            for (var index = assets.Length - 1; index >= 0; index--)
            {
                if (assets[index].name != assetName) continue;
                if (assets[index] is T) return (T) assets[index];
            }

            return null;
        }

        public static T FindAsset<T>(T[] assets, string assetName) where T : Object
        {
            if (null == assets || assets.Length == 0) return null;
            for (var index = assets.Length - 1; index >= 0; index--)
            {
                if (assets[index].name == assetName) return assets[index];
            }

            return null;
        }

        public static Object[] FindAssets(Object[] assets, string[] assetNames)
        {
            if (null == assets || assets.Length == 0) return null;
            if (null == assetNames || assetNames.Length == 0) return null;
            var rs = new Object[assetNames.Length];
            for (var index = 0; index < rs.Length; index++)
            {
                var n = assetNames[index];
                var asset = Array.Find(assets, o => o.name == n);
                rs[index] = asset;
            }

            return rs;
        }

        public static Object[] FindAssets(Object[] assets, string[] assetNames, Type type)
        {
            if (null == assets || assets.Length == 0) return null;
            if (null == assetNames || assetNames.Length == 0) return null;
            var rs = new Object[assetNames.Length];
            for (var index = 0; index < rs.Length; index++)
            {
                var n = assetNames[index];
                var asset = Array.Find(assets, o => o.name == n && o.GetType() == type);
                rs[index] = asset;
            }

            return rs;
        }

        public static T[] FindAssets<T>(Object[] assets, string[] assetNames) where T : Object
        {
            if (null == assets || assets.Length == 0) return null;
            if (null == assetNames || assetNames.Length == 0) return null;
            var rs = new T[assetNames.Length];
            for (var index = 0; index < rs.Length; index++)
            {
                var n = assetNames[index];
                var asset = Array.Find(assets, o => o.name == n && o is T);
                rs[index] = (T) asset;
            }

            return rs;
        }

        public static T[] FindAssets<T>(T[] assets, string[] assetNames) where T : Object
        {
            if (null == assets || assets.Length == 0) return null;
            if (null == assetNames || assetNames.Length == 0) return null;
            var rs = new T[assetNames.Length];
            for (var index = 0; index < rs.Length; index++)
            {
                var n = assetNames[index];
                var asset = Array.Find(assets, (o => o.name == n));
                rs[index] = asset;
            }

            return rs;
        }

        /// <summary>
        /// Unity建议使用相对路径的绝对模式(包含扩展名)
        /// </summary>
        /// <param name="baseLocUri"></param>
        /// <param name="path"></param>
        /// <returns></returns>
        public static string GetAssetRelativeFullPath(string baseLocUri, string path)
        {
            var rs = UnityPathUtil.CombineUnityPath(baseLocUri, path);
//            DebugUtil.Log("InnerGetAssetName:", rs);
            return rs;
        }
    }

    /// <summary>
    /// 内部路径记录
    /// </summary>
    internal sealed class LoaderBaseInfo
    {
        private string m_BaseReqUri = "";
        private string m_BaseLocUri = "";

        private string m_CacheName = "";
        private string m_CachePath = "";

        public string BaseReqUri => m_BaseReqUri;

        public string BaseLocUri => m_BaseLocUri;

        public string CacheName => m_CacheName;

        public string CachePath => m_CachePath;

        public void SetBaseReqUri(string value)
        {
            m_BaseReqUri = value;
        }

        public void SetBaseLocUri(string value)
        {
            m_BaseLocUri = value;
        }

        public void SetCacheName(string value)
        {
            m_CacheName = value;
        }

        public void SetCachePath(string value)
        {
            m_CachePath = value;
        }

        public string GetAssetRelativeFullPath(string assetPath)
        {
            return Internal.GetAssetRelativeFullPath(m_BaseLocUri, assetPath);
        }

        public override string ToString()
        {
            return $"[BaseReqUri={m_BaseReqUri}],[BaseLocUri={m_BaseLocUri}],[CacheName={m_CacheName}],[CachePath={m_CachePath}]";
        }
    }

    /// <summary>
    /// 下载AssetBundle的集合
    /// </summary>
    internal sealed class BundleRefSet
    {
        internal const string EventReleaseBundle = "AssetBundleRefSet_EventReleaseBundle";
        internal const string EventDebugRef = "AssetBundleRefSet_EventDebugRef";
        internal static readonly IEventDispatcher Dispatcher = new EventDispatcher();

        internal static void DebugBundleRef()
        {
            Dispatcher.DispatchEvent(EventDebugRef, null);
        }

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

        private readonly List<BundleRef> m_BundleRefs = new List<BundleRef>();

        public BundleRefSet()
        {
            Init();
        }

        private void Init()
        {
            Dispatcher.AddEventListener(EventReleaseBundle, OnReleaseBundle);
            Dispatcher.AddEventListener(EventDebugRef, OnDebugRef);
        }

        public void Dispose()
        {
            Dispatcher.RemoveEventListener(EventDebugRef, OnDebugRef);
            Dispatcher.RemoveEventListener(EventReleaseBundle, OnReleaseBundle);
        }

        public BundleRef GetBundleRef(string bundleName)
        {
            if (string.IsNullOrEmpty(bundleName)) return null;
            return m_BundleRefs.Find(abRef => abRef.BundleName == bundleName);
        }

        public BundleRef GetBundleRef(string bundleName, AssetBundleStatus status)
        {
            if (string.IsNullOrEmpty(bundleName)) return null;
            return m_BundleRefs.Find(abRef => abRef.BundleName == bundleName && (status & abRef.Status) > 0);
        }

        public void AddRef(BundleRef @ref)
        {
            if (null == @ref) return;
            var find = m_BundleRefs.Find((@ref.Equals));
            if (null != find) return;
            m_BundleRefs.Add(@ref);
        }

        public void RemoveRef(BundleRef @ref)
        {
            DoRemoveRef(@ref);
        }

        public void Release(string bundleName)
        {
            var abRef = GetBundleRef(bundleName);
            abRef?.Release();
        }

        public void Release(string[] bundleNames)
        {
            if (null == bundleNames || bundleNames.Length == 0) return;
            foreach (var bundleName in bundleNames)
            {
                Release(bundleName);
            }
        }

        public void DebugPrintlnRef()
        {
            var remain = "";
            if (m_BundleRefs.Count > 0)
            {
                var sb = new StringBuilder();
                foreach (var bundleRef in m_BundleRefs)
                {
                    sb.Append(bundleRef.BundleName + ",");
                }

                sb.Remove(sb.Length - 1, 1);
                remain = sb.ToString();
            }

            DebugUtil.Log($"剩余AssetBundle引用({m_BundleRefs.Count})：", remain);
        }

        private void OnReleaseBundle(EventData evd)
        {
            var abRef = (BundleRef) evd.Data;
            DoRemoveRef(abRef);
        }

        private void OnDebugRef(EventData evd)
        {
            DebugPrintlnRef();
        }

        private void DoRemoveRef(BundleRef @ref)
        {
            m_BundleRefs.Remove(@ref);
//            DebugPrintlnRef();
        }
    }
}