using FairyGUI;
using Spine.Unity;
using System;
using System.Collections.Generic;
using UnityEngine;
using XGame.Framework.Asset;
using XGame.Framework.Asyncs;
using XGame.Framework.Interfaces;
using XGame.Framework.UI;

namespace XGame.Framework.FGUI
{
    internal partial class FguiModule : IUIModule, IUpdate, ILateUpdate, IUIContextSetter, IDisposable, IUIAssetModuleCreator, IPackageHandle
    {

        private UIContext _context;
        private IUITree _uitree;
        private IAssetModule _assetModule;
        /// <summary>
        /// 加载中的
        /// </summary>
        private Dictionary<string, IAsync> _loadingMap;
        /// <summary>
        /// 已打开的UI
        /// </summary>
        private Dictionary<string, IUIView> _openedMap;
        /// <summary>
        /// 已关闭的UI
        /// </summary>
        private Dictionary<string, IUIView> _closedMap;
        /// <summary>
        /// Update 和 LateUpdate用
        /// </summary>
        private List<IUIView> _updates;
        public FguiModule(IAssetModule assetModule, IUITree uitree)
        {
            _assetModule = assetModule;
            _uitree = uitree;
            _loadingMap = new Dictionary<string, IAsync>();
            _openedMap = new Dictionary<string, IUIView>();
            _closedMap = new Dictionary<string, IUIView>();
            _updates = new List<IUIView>();
            //Gtween的默认曲线
            GTweener.defaultEaseType = EaseType.Linear;
            #region 只对PackageItem有效
            NTexture.CustomDestroyMethod += RecycleTexture;
            NAudioClip.CustomDestroyMethod += RecycleAudio;
            GLoader3D.CustomLoadSpine += OnLoadSpine;
            GLoader3D.CustomSpineDestroyMethod += RecycleSpine;
            #endregion
            UIObjectFactory.SetLoaderExtension(CreatCustomLoader);
        }

        public UIContext Context { get => _context; set => _context = value; }
        #region 接口实现
        Camera IUIModule.Camera => _uitree.Camera;


        Canvas IUIModule.GetCanvas(UILayer layer)
        {
            return _uitree.GetCanvas(layer);
        }

        IAsync IUIModule.OpenAsync(UIKey uikey, object intent)
        {
            string key = uikey;
            if (_loadingMap.ContainsKey(key))
            {
                return _loadingMap[key];
            }
            var asyncGroup = new AsyncGroup();
            if (_openedMap.ContainsKey(key))
            {
                Log.Warn($"重复开启UI:{key}");
            }
            else if (_closedMap.TryGetValue(key, out var closedView))
            { // 已关闭的UI
                asyncGroup.On(_ =>
                {
                    _closedMap.Remove(key);
                    _uitree.SetAsLastSibling(closedView.Panel);
                    closedView.Enable(intent);
                    _openedMap.Add(key, closedView);
                    FrameworkEvent.Instance.Notify(EventDefine.UI_OPENED, uikey);
                });
            }
            else
            {
                _loadingMap.Add(key, asyncGroup);
                var objLoadAsync = new GObjectFromPackageAsync(uikey.PackageName, uikey.PanelName);
                objLoadAsync.Join(asyncGroup);
                objLoadAsync.On(_ =>
                {
                    _loadingMap.Remove(key);
                    var panelObj = objLoadAsync.Result as GComponent;
                    if (panelObj == null)
                    {
                        Log.Error($"UI加载结果为空. UIKey:{uikey}");
                        return;
                    }
                    var panel = new FguiPanel(panelObj, GetLayer(uikey.UIViewType));
                    var view = Activator.CreateInstance(uikey.UIViewType) as IUIView;
                    var context = _context.Clone();
                    (context as IUIViewAdapter).Key = uikey;
                    (view as UIView).Init(context, panel);
                    _uitree.AddPanel(panel);
                    view.Enable(intent);
                    _openedMap.Add(key, view);
                    FrameworkEvent.Instance.Notify(EventDefine.UI_OPENED, uikey);
                });
                var pkgLoadAsync = LoadPackageAsync(uikey);
                if (pkgLoadAsync != null)
                {
                    pkgLoadAsync.Join(asyncGroup);
                    pkgLoadAsync.On(_ =>
                    {
                        objLoadAsync.Start();
                    });
                }
                else
                {
                    objLoadAsync.Start();
                }
            }
            asyncGroup.End();
            return asyncGroup;
        }
        void IUIModule.Close(UIKey uikey, bool isDestroy)
        {
            var key = uikey.Key;
            if (_loadingMap.TryGetValue(key, out var async))
            {
                _loadingMap.Remove(key);
                async.RemoveAll();
                return;
            }
            if (_openedMap.TryGetValue(key, out var uiView))
            {
                _openedMap.Remove(key);
                uiView.Disable();
                _uitree.RemovePanel(uiView.Panel);
                if (isDestroy)
                {
                    DestroyView(uiView);
                }
                else
                {
                    _closedMap.Add(key, uiView);
                }
                FrameworkEvent.Instance.Notify(EventDefine.UI_CLOSED, uikey);
                return;
            }
            if (isDestroy && _closedMap.TryGetValue(key, out uiView))
            {
                _closedMap.Remove(key);
                DestroyView(uiView);
            }
        }
        IAsync IUIModule.Preload(UIKey uikey)
        {
            var loadAsync = LoadPackageAsync(uikey);
            return loadAsync;
        }

        void IUpdate.Update(int millisecond)
        {
            if (_openedMap.Count == 0)
                return;
            _updates.Clear();
            _updates.AddRange(_openedMap.Values);
            for (var i = 0; i < _updates.Count;)
            {
                var view = _updates[i];
                view.Update(millisecond);
                if (view.Active)
                {
                    i++;
                }
                else
                {
                    _updates.RemoveAt(i);
                }
            }
        }

        void ILateUpdate.LateUpdate(int millisecond)
        {
            if (_updates.Count == 0)
                return;
            foreach (var uiview in _updates)
            {
                uiview.LateUpdate(millisecond);
            }
            _updates.Clear();
        }

        void IDisposable.Dispose()
        {
            _updates.Clear();
            foreach (var async in _loadingMap.Values)
            {
                async.RemoveAll();
            }
            _loadingMap.Clear();
            foreach (var uiview in _closedMap.Values)
            {
                DestroyView(uiview);
            }
            _closedMap.Clear();
            foreach (var uiview in _openedMap.Values)
            {
                DestroyView(uiview);
            }
            _openedMap.Clear();
            #region 清理FGUI緩存
            StageEngine.beingQuit = true;
            UIPackage.RemoveAllPackages();
            Stage.inst.Dispose();
            NTexture.CustomDestroyMethod -= RecycleTexture;
            NAudioClip.CustomDestroyMethod -= RecycleAudio;
            GLoader3D.CustomLoadSpine -= OnLoadSpine;
            GLoader3D.CustomSpineDestroyMethod -= RecycleSpine;
            UIObjectFactory.Clear();
            ClearGloaderAssets();
            #endregion
            (_uitree as IDisposable)?.Dispose();
            (_assetModule as IDisposable)?.Dispose();
            _assetModule = null;
            _context = null;
        }
        #endregion
        #region IPackageHandle 接口实现
        public IGObjectLoadAsync LoadGobjectAsync(UIKey uikey)
        {
            var objLoadAsync = new GObjectFromPackageAsync(uikey.PackageName, uikey.PanelName);
            var pkgLoadAsync = LoadPackageAsync(uikey);
            if (pkgLoadAsync != null)
            {
                pkgLoadAsync.On(_ =>
                {
                    objLoadAsync.Start();
                });
            }
            else
            {
                objLoadAsync.Start();
            }
            return objLoadAsync;
        }
        /// <summary>
        /// 加载UIPackage
        /// </summary>
        /// <param name="uikey"></param>
        /// <returns></returns>
        public IAsync LoadPackageAsync(UIKey uikey)
        {
            if (UIPackage.GetByName(uikey.PackageName) == null)
            {
                var pkgAddressable = FguiUtils.ToDescFileName(uikey.PackageName);
                if (_loadingMap.ContainsKey(pkgAddressable))
                {
                    return _loadingMap[pkgAddressable];
                }
                var pkgLoadAsync = _assetModule.LoadAsync<TextAsset>(pkgAddressable);
                _loadingMap.Add(pkgAddressable, pkgLoadAsync);
                pkgLoadAsync.On(_ =>
                {
                    _loadingMap.Remove(pkgAddressable);
                    var pkgAsset = pkgLoadAsync.Result;
                    var descData = pkgAsset?.bytes;
                    _assetModule.Recycle(pkgAsset);
                    if (descData == null || descData.Length == 0)
                    {
                        Log.Error($"UIPackage 加载失败. UIKey:{uikey}");
                        return;
                    }
                    if (UIPackage.GetByName(uikey.PackageName) == null)
                    {
                        UIPackage.AddPackage(descData, string.Empty, LoadPackageItemAsync);
                        //var pkg = UIPackage.GetByName(uikey.PackageName);
                        //if (pkg != null && pkg.dependencies != null)
                        //{
                        //    Log.Debug($"{uikey.PackageName} dependencies:{pkg.dependencies.Length}");
                        //    foreach (var dependency in pkg.dependencies)
                        //    {
                        //        foreach(var item in dependency)
                        //        {
                        //            Log.Debug($"dependency key:{item.Key} value:{item.Value}");
                        //        }
                        //    }
                        //}
                    }
                });
                return pkgLoadAsync;
            }
            else
            {
                Log.Info($"UIPackage已加载. UIKey:{uikey}");
                return null;
            }
        }
        #endregion
        IUIAssetModule IUIAssetModuleCreator.Create(UIContext context)
        {
            return new FguiAssetModule(context, this);
        }
        #region 内部函数

        /// <summary>
        /// 异步加载PackageItem
        /// UIPackage的Texture、AudioClip等资源
        /// </summary>
        /// <param name="name"></param>
        /// <param name="extension"></param>
        /// <param name="type"></param>
        /// <param name="item"></param>
        private void LoadPackageItemAsync(string name, string extension, System.Type type, PackageItem item)
        {
            Log.Info($"LoadPackageItemAsync name:{name} extension:{extension} type:{type} itemName:{item.name}");
            var addressable = $"{item.owner.name}_{name}";
            var loadAsync = _assetModule.LoadAsync<UnityEngine.Object>(addressable);
            loadAsync.On(_ =>
            {
                var asset = loadAsync.Result;
                item.owner.SetItemAsset(item, Convert(asset, type), DestroyMethod.Custom);
            });

        }
        private object Convert(UnityEngine.Object value, Type type)
        {
            if (value == null)
                return default;
            if (type.IsInstanceOfType(value))
            {
                return value;
            }
            if (type.IsClass)
            {
                if (type.IsSubclassOf(typeof(Component)))
                {
                    var go = value as GameObject;
                    if (go != null)
                    {
                        return go.GetComponent(type);
                    }
                    var component = value as Component;
                    if (component != null)
                    {
                        return component.gameObject.GetComponent(type);
                    }
                }
                else if (type == typeof(Sprite) && value is Texture2D texture)
                {
                    object sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
                    return sprite;
                }
            }
            Log.Error($"UIModuleFGUI object conver type Error. source type:{value.GetType()} name:{value} target:{type}");
            return value;
        }
        private void DestroyView(IUIView view)
        {
            var uiPanel = (view.Panel as FguiPanel).Panel;
            (view as IDisposable).Dispose();
            uiPanel.Dispose();
        }
        /// <summary>
        /// 只对LoadPackageItemAsync加载的资源有效
        /// </summary>
        /// <param name="texture"></param>
        private void RecycleTexture(Texture texture)
        {
            Log.Info($"RecycleTexture Texture:{texture.name}");
            _assetModule.Recycle(texture);
        }
        /// <summary>
        /// 只对LoadPackageItemAsync加载的资源有效
        /// </summary>
        /// <param name="audioClip"></param>
        private void RecycleAudio(AudioClip audioClip)
        {
            Log.Info($"RecycleAudio AudioClip:{audioClip.name}");
            _assetModule?.Recycle(audioClip);
        }
        private GLoader CreatCustomLoader()
        {
            return new CustomLoader(this);
        }
        private void OnLoadSpine(string addressable, Action<SkeletonDataAsset> action)
        {
            var loadAsync = _assetModule.LoadAsync<SkeletonDataAsset>(addressable);
            loadAsync.On(_ =>
            {
                var data = loadAsync.Result;
                if (data == null)
                {
                    return;
                }
                action(data);
            });
        }
        private void RecycleSpine(SkeletonDataAsset asset)
        {
            _assetModule.Recycle(asset);
        }
        private UILayer GetLayer(Type uiviewType)
        {
            var layer = UILayer.Normal;
            var property = uiviewType.GetProperty("Layer", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.IgnoreCase);
            if (property != null)
            {
                layer = (UILayer)property.GetValue(null);
            }
            return layer;
        }
        #endregion
    }
}