﻿using System.Collections.Generic;
using JLGames.RocketDriver.CSharp.Utils;
using UnityEngine;

namespace JLGames.RocketDriver.Actions.Loaderx
{
    public interface ILoaderCacheManager
    {
        /// <summary>
        /// Set cahce disable or not
        /// 设置缓存是否启用
        /// </summary>
        /// <param name="disable"></param>
        void SetDisableCache(bool disable);

        /// <summary>
        /// Set max size of history cache path. 
        /// 设置保留缓存目录跳转历史的最大个数
        /// </summary>
        /// <param name="size"></param>
        void SetHistorySize(int size);

        /// <summary>
        /// Set max size of cache version.
        /// 设置保留资源版本最大个数
        /// </summary>
        /// <param name="size"></param>
        void SetVersionSize(int size);

        /// <summary>
        /// Clear overflowed version cache except for the hash
        /// 清除溢出的版本缓存
        /// </summary>
        /// <param name="bundleName">AssetBujdle的名称</param>
        /// <param name="keepVersion">保留版本号</param>
        void ClearOverVersions(string bundleName, Hash128 keepVersion);

        /// <summary>
        /// Clear overflowed version cache
        /// 清除溢出的版本缓存
        /// </summary>
        /// <param name="bundleName">AssetBujdle的名称</param>
        void ClearOverVersions(string bundleName);

        /// <summary>
        /// Check cache existence
        /// 检查缓存存在性
        /// </summary>
        /// <param name="cacheName"></param>
        /// <param name="cachePath"></param>
        /// <param name="or"></param>
        /// <returns></returns>
        bool CheckCacheExist(string cacheName, string cachePath, bool or);

        /// <summary>
        /// Add a cache configuration
        /// 增加一个缓存配置
        /// </summary>
        /// <param name="cacheName">缓存配置名称，具有唯一性</param>
        /// <param name="cachePath">缓存路径</param>
        /// <param name="setCurrent"></param>
        /// <returns></returns>
        ILoaderCache AddCache(string cacheName, string cachePath, bool setCurrent = true);

        /// <summary>
        /// Delete cache configuration, cached data is retained
        /// 删除缓存配置，已经缓存的数据保留
        /// </summary>
        /// <param name="cacheName"></param>
        /// <returns></returns>
        ILoaderCache RemoveCache(string cacheName);

        /// <summary>
        /// Get cache configuration
        /// 取缓存配置
        /// </summary>
        /// <param name="cacheName"></param>
        /// <returns></returns>
        ILoaderCache GetCache(string cacheName);

        /// <summary>
        /// Locate the specified cache
        /// 定位到指定缓存
        /// </summary>
        /// <param name="cacheName"></param>
        /// <returns></returns>
        ILoaderCache GotoCache(string cacheName);

        /// <summary>
        /// Return to the previous cache based on history
        /// 根据历史记录返回上一级缓存
        /// </summary>
        /// <returns></returns>
        ILoaderCache GoPrevCache();

        /// <summary>
        /// Current cache
        /// 当前的缓存
        /// </summary>
        ILoaderCache CurrentCache { get; }
    }

    internal class LoaderCacheManager : ILoaderCacheManager
    {
        private bool m_DisableCache;
        private int m_HistorySize = 3;
        private int m_VersionSize = 1;

        private readonly List<ILoaderCache> m_CacheList = new List<ILoaderCache>();
        private string m_Current;
        private readonly List<string> m_History = new List<string>();

        public void SetDisableCache(bool disable)
        {
            m_DisableCache = disable;
        }

        public void SetHistorySize(int size)
        {
            m_HistorySize = size;
        }

        public void SetVersionSize(int size)
        {
            m_VersionSize = size;
        }

        public void ClearOverVersions(string bundleName, Hash128 keepVersion)
        {
            if (m_DisableCache)
            {
                return;
            }

            InnerCurrentCache?.ClearOverVersions(bundleName, m_VersionSize, keepVersion);
        }

        public void ClearOverVersions(string bundleName)
        {
            if (m_DisableCache)
            {
                return;
            }

            InnerCurrentCache?.ClearOverVersions(bundleName, m_VersionSize);
        }

        public bool CheckCacheExist(string cacheName, string cachePath, bool or = true)
        {
            return InnerFindCache(cacheName, cachePath, or) != -1;
        }

        public ILoaderCache AddCache(string cacheName, string cachePath, bool setCurrent = true)
        {
            if (m_DisableCache || string.IsNullOrEmpty(cachePath))
            {
                return null;
            }

            if (InnerFindCache(cacheName, cachePath) != -1)
            {
                return null;
            }

            DirectoryUtil.MakeDir(cachePath, true);

            var c = Caching.AddCache(cachePath);
            ILoaderCache uc = new LoaderCache(cacheName, c);
            m_CacheList.Add(uc);
            if (setCurrent)
            {
                InnerGotoCache(cacheName);
            }

            return uc;
        }

        public ILoaderCache RemoveCache(string cacheName)
        {
            if (m_DisableCache)
            {
                return null;
            }

            var idx = InnerFindCache(cacheName);
            if (-1 == idx)
            {
                return null;
            }

            var rs = m_CacheList[idx];
            m_CacheList.RemoveAt(idx);
            for (int i = m_History.Count - 1; i >= 0; i--)
            {
                if (m_History[i] == cacheName)
                {
                    m_History.RemoveAt(i);
                }
            }

            if (cacheName == m_Current)
            {
                if (m_History.Count == 0)
                {
                    m_Current = "";
                }
                else
                {
                    var prev = InnerGoPrevCache();
                    m_Current = prev.CacheName;
                }
            }

            return rs;
        }

        public ILoaderCache GetCache(string cacheName)
        {
            var idx = InnerFindCache(cacheName);
            if (-1 == idx)
            {
                return null;
            }

            return m_CacheList[idx];
        }

        public ILoaderCache GotoCache(string cacheName)
        {
            if (m_DisableCache)
            {
                return null;
            }

            if (InnerIsCurrentCache(cacheName))
            {
                return InnerCurrentCache;
            }

            if (-1 == InnerFindCache(cacheName))
            {
                return null;
            }

            return InnerGotoCache(cacheName);
        }

        public ILoaderCache GoPrevCache()
        {
            if (m_DisableCache)
            {
                return null;
            }

            return m_History.Count == 0 ? null : InnerGoPrevCache();
        }

        public ILoaderCache CurrentCache => InnerCurrentCache;

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

        private ILoaderCache InnerGotoCache(string cacheName)
        {
            var idx = InnerFindCache(cacheName);
            if (-1 == idx)
            {
                return null;
            }

            // 保存历史
            var crr = InnerCurrentCache;
            if (null == crr)
            {
                return InnerLocateToCurrent(idx);
            }

            m_History.Add(crr.CacheName);
            while (m_History.Count > m_HistorySize)
            {
                m_History.RemoveAt(0);
            }

            return InnerLocateToCurrent(idx);
        }

        private ILoaderCache InnerGoPrevCache()
        {
            var name = m_History[m_History.Count - 1];
            m_History.RemoveAt(m_History.Count - 1);

            var idx = InnerFindCache(name);
            return InnerLocateToCurrent(idx);
        }

        private ILoaderCache InnerLocateToCurrent(int idx)
        {
            if (-1 == idx)
            {
                Caching.currentCacheForWriting = Caching.defaultCache;
                m_Current = "";
                return null;
            }

            var c = m_CacheList[idx];
            Caching.currentCacheForWriting = c.Cache;
            m_Current = m_CacheList[idx].CacheName;
            return c;
        }

        private int InnerFindCache(string cacheName, string cachePath = "", bool or = true)
        {
            for (var i = 0; i < m_CacheList.Count; i++)
            {
                if (m_CacheList[i].CacheName != cacheName)
                {
                    continue;
                }

                if (or || m_CacheList[i].Cache.path == cachePath)
                {
                    return i;
                }
            }

            return -1;
        }

        private bool InnerIsCurrentCache(string cacheName)
        {
            return m_Current == cacheName && "" != m_Current;
        }

        private ILoaderCache InnerCurrentCache
        {
            get
            {
                if ("" == m_Current || m_CacheList.Count == 0)
                {
                    return null;
                }

                var idx = InnerFindCache(m_Current);
                return -1 == idx ? null : m_CacheList[idx];
            }
        }
    }
}