using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using Object = UnityEngine.Object;

namespace Guru
{
    public enum ResType
    {
        Unknown = 0,
        Bundle,
        Texture,
        File,
    }

    public class ResUriHelper
    {
        public const string FilePrefix = "file::";
        public const string BundlePrefix = "bundle::";
        public const string TexturePrefix = "texture::";
        public const string HttpPrefix = "http://";
        public const string HttpsPrefix = "https://";
        
        
        
        public static string GetFileUri(string url)
        {
            return $"{FilePrefix}{url}";
        }
        
        public static string GetBundleUri(string url)
        {
            return $"{BundlePrefix}{url}";
        }
        
        public static string GetTextureUri(string url)
        {
            return $"{TexturePrefix}{url}";
        }
        
        public static void UriToType(string uri, out ResType type, out string url)
        {
            type = ResType.Unknown;
            url = uri;
            if (uri.StartsWith(FilePrefix))
            {
                type = ResType.File;
                url = uri.Substring(FilePrefix.Length);
            }
            else if (uri.StartsWith(BundlePrefix))
            {
                type = ResType.Bundle;
                url = uri.Substring(BundlePrefix.Length);
            }
            else if (uri.StartsWith(TexturePrefix))
            {
                type = ResType.Texture;
                url = uri.Substring(TexturePrefix.Length);
            }
            else if (uri.StartsWith(HttpPrefix) || uri.StartsWith(HttpsPrefix))
            {
                type = ResType.File;
                url = uri;
            }
        }
    }


    public class ResLoaderBase: IResLoader
    {
        public int RetryTimes { get; set; } = 3;
        private bool _iSBusy = false;
        public bool IsBusy
        {
            get => _iSBusy;
            set
            {
                _iSBusy = value;
                if(!_iSBusy) OnLoaderFree?.Invoke(this);
            }
        }

        public Action<ResLoaderBase> OnLoaderFree;

        private Queue<OneResLoadTask> _taskPool = new Queue<OneResLoadTask>(10);
        

        #region 加载逻辑

        internal static OneResLoadTask CreateBundleTask(string url, Action<ResLoadInfo> callback, Action<bool, OneResLoadTask> handler)
        {
            return OneResLoadTask.Create(url, callback, handler, UnityWebRequestAssetBundle.GetAssetBundle(url));
        }

        internal static OneResLoadTask CreateTextureTask(string url, Action<ResLoadInfo> callback, Action<bool, OneResLoadTask> handler)
        {
            return OneResLoadTask.Create(url, callback, handler, UnityWebRequestTexture.GetTexture(url, true));;
        }
        
        internal static OneResLoadTask CreateFileTask(string url, Action<ResLoadInfo> callback, Action<bool, OneResLoadTask> handler)
        {
            return OneResLoadTask.Create(url, callback, handler, new UnityWebRequest(url));;
        }
        
        
        /// <summary>
        /// 异步加载
        /// </summary>
        /// <param name="resUri"></param>
        /// <param name="onLoadComplete"></param>
        public void LoadAsync(string resUri, Action<ResLoadInfo> onLoadComplete)
        {
            ResUriHelper.UriToType(resUri, out var type, out var url);
            switch (type)
            {
                case ResType.Bundle:
                    LoadBundle(url, onLoadComplete);
                    break;
                case ResType.Texture:
                    LoadTexture(url, onLoadComplete);
                    break;
                case ResType.File:
                    LoadFile(url, onLoadComplete);
                    break;
                default:
                    Debug.LogError($"--- Unknown ResType: {type}");
                    break;
            }
        }
        
        /// <summary>
        /// 开始异步加载
        /// </summary>
        /// <param name="task"></param>
        /// <param name="onLoadComplete"></param>
        private void StartAsyncLoading(OneResLoadTask task)
        {
            IsBusy = true;
            task.www.SendWebRequest().completed += ao =>
            {
                if (task.www.result == UnityWebRequest.Result.Success)
                {
                    task.loadHandler?.Invoke(true, task);
                }
                else
                {
                    if (task.retryTimes < RetryTimes)
                    {
                        task.retryTimes++;
                        StartAsyncLoading(task);
                    }
                    else
                    {
                        task.loadHandler?.Invoke(false, task);
                    }
                }
            };
        }

        private void AddLoadTask(OneResLoadTask task)
        {
            if (IsBusy)
            {
                _taskPool.Enqueue(task); // 入队等待
            }
            else
            {
                StartAsyncLoading(task); // 直接加载
            }
        }
        
        
        #endregion
        
        #region 加载 Bundle 资源

        /// <summary>
        /// 加载 Bundle
        /// </summary>
        /// <param name="url"></param>
        /// <param name="onLoadComplete"></param>
        public void LoadBundle(string url, Action<ResLoadInfo> onLoadComplete)
        {
            AddLoadTask(CreateBundleTask(url, onLoadComplete, OnBundleLoaded));
        }
        private void OnBundleLoaded(bool success, OneResLoadTask task)
        {

            var info = new ResLoadInfo(task.url);
            if (success)
            {
                var ab = DownloadHandlerAssetBundle.GetContent(task.www);
                if(ab != null)
                {
                    info.success = true;
                    info.target = ab;
                    info.bundleName = ab.name;
                }
            }
            task.callback?.Invoke(info);
            SetFree();
        }
        
        #endregion

        #region 加载二进制文件

        public void LoadFile(string url, Action<ResLoadInfo> onLoadComplete)
        {
            if (IsBusy)
            {
                Debug.LogError($"--- Loader is busy, canot load this task");
                return; 
            }
            
            StartAsyncLoading(CreateFileTask(url, onLoadComplete, OnFileLoaded));
        }

        private void OnFileLoaded(bool success, OneResLoadTask task)
        {
            var info = new ResLoadInfo(task.url);
            if (success)
            {
                var bytes = task.www.downloadHandler?.data ?? null;
                if(bytes != null)
                {
                    info.success = true;
                    info.target = bytes;
                }
            }
            task.callback?.Invoke(info);
            SetFree();
        }
        
        #endregion

        #region 加载图片

        
        public void LoadTexture(string url, Action<ResLoadInfo> onLoadComplete)
        {
            var request = CreateTextureTask(url, onLoadComplete, OnTextureLoaded);
            if (!IsBusy)
            {
                StartAsyncLoading(request);
            }
            _taskPool.Enqueue(request);
             
        }
        void OnTextureLoaded(bool success, OneResLoadTask task)
        {
            var info = new ResLoadInfo(task.url);
            if (success)
            {
                var texture = DownloadHandlerTexture.GetContent(task.www);
                if (texture != null)
                {
                    info.success = true;
                    info.target = texture;
                }
            }
            task.callback?.Invoke(info);
            SetFree();
        }

        #endregion
        
        #region 加载器空闲


        private void SetFree()
        {
            if (_taskPool.Count > 0)
            {
                StartAsyncLoading(_taskPool.Dequeue());
            }
            IsBusy = false;
        }


        #endregion
        
        
    }

    /// <summary>
    /// 单挑加载任务类
    /// </summary>
    internal struct OneResLoadTask
    {
        public string url;
        public int retryTimes;
        public Action<ResLoadInfo> callback;
        public UnityWebRequest www;
        public Action<bool, OneResLoadTask> loadHandler;
        
        
        public static OneResLoadTask Create(string url, Action<ResLoadInfo> callback, Action<bool, OneResLoadTask> handler,
            UnityWebRequest www)
        {
            return new OneResLoadTask()
            {
                url = url,
                callback = callback,
                loadHandler = handler,
                www = www,
                retryTimes = 0,
            };
        }
    }

}