#if FAIRYGUI_SPINE

using System;
using System.Collections.Generic;
using UnityEngine;
using Spine.Unity;

namespace FairyGUI
{
    /// <summary>
    /// 
    /// </summary>
    public partial class GLoader3D : GObject
    {
        public static Action<SkeletonDataAsset> CustomSpineDestroyMethod;
        public static Action<string, Action<SkeletonDataAsset>> CustomLoadSpine;
        
        SkeletonAnimation _spineAnimation;
        /// <summary>
        /// 
        /// </summary>
        /// <value></value>
        public SkeletonAnimation spineAnimation
        {
            get { return _spineAnimation; }
        }
        /// <summary>
        /// 加载spine并设置动作
        /// </summary>
        /// <param name="url"></param>
        /// <param name="animationName"></param>
        /// <param name="isLoop"></param>
        public void LoadSpine(string url, string animationName, bool isLoop = false)
        {
            // loop和animationName先赋值，避免url加载结束(已加载过的资源异步回调可能同步回来)回调时_animationName为空导致动作设置失败
            loop = isLoop;
            this.animationName = animationName;
            this.url = url;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="asset"></param>
        public void SetSpine(SkeletonDataAsset asset)
        {
            SetSpine(asset, _contentItem.width, _contentItem.height, _contentItem.skeletonAnchor);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="asset"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="anchor">本地坐标，fguid的锚点在左上角</param>
        public void SetSpine(SkeletonDataAsset asset, int width, int height, Vector2 anchor)
        {
            SetSpine(asset, width, height, anchor, true);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="asset"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="anchor"></param>
        /// <param name="cloneMaterial"></param>
        public void SetSpine(SkeletonDataAsset asset, int width, int height, Vector2 anchor, bool cloneMaterial)
        {
            if (_spineAnimation != null)
                FreeSpine();

            _content.customCloneMaterials = MaterialOverride;
            _content.customRecoverMaterials = CleanMaterialOverride;

            _spineAnimation = SkeletonRenderer.NewSpineGameObject<SkeletonAnimation>(asset);
            _spineAnimation.gameObject.name = asset.name;
            Spine.SkeletonData dat = asset.GetSkeletonData(false);
            _spineAnimation.gameObject.transform.localScale = new Vector3(1 / asset.scale, 1 / asset.scale, 1);
            _spineAnimation.gameObject.transform.localPosition = new Vector3(anchor.x, -anchor.y, 0);
            SetWrapTarget(_spineAnimation.gameObject, cloneMaterial, width, height);

            _spineAnimation.skeleton.R = _color.r;
            _spineAnimation.skeleton.G = _color.g;
            _spineAnimation.skeleton.B = _color.b;

            OnChangeSpine(null);
        }

        protected void LoadSpine()
        {
            SkeletonDataAsset asset = (SkeletonDataAsset)_contentItem.skeletonAsset;
            if (asset == null)
                return;

            SetSpine(asset, _contentItem.width, _contentItem.height, _contentItem.skeletonAnchor);
        }

        protected void OnChangeSpine(string propertyName)
        {
            if (_spineAnimation == null)
                return;

            if (propertyName == "color")
            {
                _spineAnimation.skeleton.R = _color.r;
                _spineAnimation.skeleton.G = _color.g;
                _spineAnimation.skeleton.B = _color.b;
                return;
            }

            var skeletonData = _spineAnimation.skeleton.Data;

            var state = _spineAnimation.AnimationState;
            Spine.Animation animationToUse = !string.IsNullOrEmpty(_animationName) ? skeletonData.FindAnimation(_animationName) : null;
            if (animationToUse != null)
            {
                var trackEntry = state.GetCurrent(0);
                if (trackEntry == null || trackEntry.Animation.Name != _animationName || trackEntry.IsComplete && !trackEntry.Loop)
                    trackEntry = state.SetAnimation(0, animationToUse, _loop);
                else
                    trackEntry.Loop = _loop;

                if (_playing)
                    trackEntry.TimeScale = 1;
                else
                {
                    trackEntry.TimeScale = 0;
                    trackEntry.TrackTime = Mathf.Lerp(0, trackEntry.AnimationEnd - trackEntry.AnimationStart, _frame / 100f);
                }
            }
            else
                state.ClearTrack(0);

            var skin = !string.IsNullOrEmpty(skinName) ? skeletonData.FindSkin(skinName) : skeletonData.DefaultSkin;
            if (skin == null && skeletonData.Skins.Count > 0)
                skin = skeletonData.Skins.Items[0];
            if (_spineAnimation.skeleton.Skin != skin)
            {
                _spineAnimation.skeleton.SetSkin(skin);
                _spineAnimation.skeleton.SetSlotsToSetupPose();
            }
        }

        protected void FreeSpine()
        {
            if (_spineAnimation != null)
            {
                if (_spineAnimation.skeletonDataAsset != null)
                {
                    if (CustomSpineDestroyMethod != null)
                    {
                        CustomSpineDestroyMethod(_spineAnimation.skeletonDataAsset);
                    }
                    else
                    {
                        Resources.UnloadAsset(_spineAnimation.skeletonDataAsset);
                    }
                }
                if (Application.isPlaying)
                    GameObject.Destroy(_spineAnimation.gameObject);
                else
                    GameObject.DestroyImmediate(_spineAnimation.gameObject);

                _content.customCloneMaterials = null;
                _content.customRecoverMaterials = null;
            }
        }

        protected void OnUpdateSpine(UpdateContext context)
        {
            if (_spineAnimation != null)
                _spineAnimation.skeleton.A = context.alpha * _content.alpha;
        }

        private void MaterialOverride(Dictionary<Material, Material> materials)
        {
            if (_spineAnimation != null)
            {
                foreach (var kv in materials)
                {
                    _spineAnimation.CustomMaterialOverride[kv.Key] = kv.Value;
                }
            }
        }

        private void CleanMaterialOverride()
        {
            if (_spineAnimation != null)
                _spineAnimation.CustomMaterialOverride.Clear();
        }
        /// <summary>
        /// spine不为空才生效
        /// </summary>
        /// <param name="animationName"></param>
        /// <param name="isLoop"></param>
        public bool Play(string animationName, bool isLoop)
        {
            if (_spineAnimation == null || this.animationName == animationName)
                return false;
            this.animationName = animationName;
            this.loop = isLoop;
            OnChangeSpine(null);
            return true;
        }
        
#if UNITY_2019_3_OR_NEWER
        [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
        static void InitializeOnLoad()
        {
            CustomSpineDestroyMethod = null;
        }
#endif
    }
}

#endif