using System.Collections.Generic;
using Games.LogicObj;
using GCGame.Table;
using Module.Log;
using UnityEngine;
using UnityEngine.Events;

namespace Games.AnimationModule
{
    // 动画机修改为正常流程,使用Trigger推动动画机内部的转换
    // AnimationLogic仅仅保证每一帧末尾将最后收到的动画触发到动画机
    // 动画机本身已经不再具备随意跳帧的功能,普攻跳帧由动画机转换配置
    public class AnimationLogic : MonoBehaviour
    {
        public bool cullAnimator
        {
            get { return _cullAnimator; }
            set
            {
                if (_cullAnimator != value)
                {
                    _cullAnimator = value;
                    RefreshCullAnimator();
                }
            }
        }
        private bool _cullAnimator = true;
        private static readonly Queue<int> _effectBuffer = new Queue<int>();
        private Animator _animationControl;
        private List<BoneData> _boneList;
        
        private Tab_Animation _inAnimator;
        // 暂时只有冻结效果,更多效果就扩展这个参数
        private AnimationFreezeData _freezeData;

        // 当前Animator中,正在播放的动画真实Hash;
        private int _inAnimatorHash;

        // 三层动画状态 - 动画机状态/希望动画机状态/当前帧希望转入状态
        // _nextInComponent搜集一帧内的转化需求,仅仅保留最终需求;
        // _toAnimator记录当前转换需求,防止重复切入循环动画,额外叠加Trigger;此外负责处理动画结束;
        // _inAnimator仅仅处理动画退出的情况。
        private Tab_Animation _nextInComponent;
        
        // 特殊跳转方式,跳转Trigger的名称;同_nextInComponent一个级别。
        private string _nextTransitionName;
        private Tab_Animation _toAnimator;
        private int _visibleRendererCount;
        public event UnityAction<Tab_Animation, string> onPlayAnim;
        public event UnityAction<int> onAnimEnter; 

        public event UnityAction<int, float> onPlayEffect;
        public event UnityAction<int> onRemoveEffect;
        public event UnityAction<int> onPlaySound;
        public event UnityAction<int> onStopSound;

        public float animationSpeed { get; private set; }
        public bool isFreeze { get; private set; }

        /// <summary>
        ///     初始化动画逻辑数据
        /// </summary>
        public void InitAnimLogicData(Animator animator, List<RendererInfo> rendererList)
        {
            _animationControl = animator;
            animationSpeed = 1f;
            isFreeze = false;
            RefreshCullAnimator();
            // 破烂动画机,消耗高,需要做CullCompletely,然而Cull状态被推两个Trigger会导致执行顺序问题;
            // 然后这破烂是否处于Cull状态还无法获得,只能自己抓全部Renderer的Visible事件来监视Animator的可视状态;
            if (rendererList == null)
                // 特殊的Bypass,给非ObjPartRoot用的
                _visibleRendererCount = 1;
            else
            {
                _visibleRendererCount = 0;
                var unityAction = new UnityAction<bool>(OnBecomeVisible);
                for (var i = 0; i < rendererList.Count; i++)
                {
                    var itemRenderer = rendererList[i].CachedRenderer;
                    if (itemRenderer != null)
                    {
                        var listener = itemRenderer.gameObject.EnsureComponent<RendererVisibleListener>();
                        if (listener.isVisible)
                            _visibleRendererCount++;
                        // 注:即使Listener被摧毁也可以在最后一帧获得OnBecomeInvisible
                        listener.onBecameVisible += unityAction;
                    }
                }
            }
            OnEnable();
        }

        public void SetAnimationSpeed(float animSpeed)
        {
            if (animationSpeed != animSpeed)
            {
                animationSpeed = animSpeed;
                RefreshAnimationSpeed();
            }
        }

        private void OnBecomeVisible(bool isVisible)
        {
            if (isVisible)
                _visibleRendererCount++;
            else
                _visibleRendererCount--;
        }

        private void SetAnimFreeze(bool freeze)
        {
            if (isFreeze != freeze)
            {
                isFreeze = freeze;
                RefreshAnimationSpeed();
            }
        }

        private void RefreshAnimationSpeed()
        {
            if (_animationControl != null)
                _animationControl.speed = isFreeze ? 0f : animationSpeed;
        }

        private void RefreshCullAnimator()
        {
            if (_animationControl != null)
                _animationControl.cullingMode = _cullAnimator
                    ? AnimatorCullingMode.CullCompletely
                    : AnimatorCullingMode.AlwaysAnimate;
        }

        // 注:物体初始化帧可能遭遇两次OnEnable。第一次由Unity触发,不存在_animationControl;第二次由InitAnimLogicData触发。
        private void OnEnable()
        {
            if (_animationControl != null)
            {
                var animatorBehavior = _animationControl.GetBehaviour<AnimationStateEvent>();
                if (animatorBehavior == null)
                    LogModule.WarningLog(string.Format("动画机没有配置状态转换事件,于物体{0}", transform.GetHierarchyName()));
                else
                {
                    animatorBehavior.OnAnimStateEnter -= OnAnimationEnter;
                    animatorBehavior.OnAnimStateEnter += OnAnimationEnter;
                    _toAnimator = TableManager.GetAnimationByID(Obj_Character.idleAnimId, 0);
                    _inAnimatorHash = GetNameHash(_toAnimator);
                }
                if (_boneList == null)
                {
                    _boneList = new List<BoneData>();
                    var root = transform.Find("Bip001");
                    if (root != null)
                    {
                        var children = root.GetComponentsInChildren<Transform>();
                        for (var i = 0; i < children.Length; i++)
                        {
                            _boneList.Add(new BoneData(children[i]));
                        }
                    }
                }
            }
        }
        
        // 防止动画速度在回收时被其他组件调整的情况
        private void OnDisable()
        {
            if (!GameManager.applicationQuit)
            {
                if (_boneList != null)
                {
                    for (var i = 0; i < _boneList.Count; i++)
                        _boneList[i].Reset();
                }
                animationSpeed = 1f;
                isFreeze = false;
                _freezeData = null;
                RefreshAnimationSpeed();
                cullAnimator = true;
            }
        }

        // 注:动画自己转向自己时,实际会先开始状态,然后再退出状态,直接End会导致新开始的特效也消失;
        // 因此特殊处理这类情况,自己切换到自己,将在开始时结束之前特效,然后开始新的;
        private void OnAnimationEnter(AnimatorStateInfo stateInfo)
        {
            _inAnimatorHash = stateInfo.shortNameHash;
            // 注:已知问题,有一些动画没有对应动画表,会导致状态错误;但是一般那些都是只有单一动画的友好NPC,所以不造成问题
            //var nextAnimData = GetStateDataByHash(stateInfo.shortNameHash);
            // 仅仅执行预期的动画转换,非预期转换直接跳过;防御上次动画因阻断未完全执行的诡异情况;
            if (_toAnimator != null && GetNameHash(_toAnimator) == stateInfo.shortNameHash)
            {
                // 注:自己转入自己时,当前normalizedTime会为转入前播放位置,不能用作时间参考
                // 自己转入自己并且跳过时间的情况没有处理
                var skipTime = _toAnimator == _inAnimator
                    ? 0f
                    : stateInfo.normalizedTime * stateInfo.length * stateInfo.speed;
                PlayStartEffect(_toAnimator, skipTime);
                ConsumeInAnimator();
                _inAnimator = _toAnimator;
                _toAnimator = null;
                if (_inAnimator.FreezeStart >= 0f && _inAnimator.FreezeDuration > 0f)
                {
                    var startTime = Time.time + _inAnimator.FreezeStart;
                    var endTime = startTime + _inAnimator.FreezeDuration;
                    _freezeData = new AnimationFreezeData(startTime, endTime);
                }
                else
                    _freezeData = null;
                SetAnimFreeze(_inAnimator.FreezeStart == 0f);
            }
            else
            // 每次转换都应该消耗掉InAnimator,只是toAnimator时需要在特殊时机消耗
            {
                ConsumeInAnimator();
            }
            if (onAnimEnter != null)
                onAnimEnter(_inAnimatorHash);
        }

        public void PlayStartEffect(Tab_Animation animData, float skipTime)
        {
            if (onPlayEffect != null)
            {
                PushEffectsToBuffer(animData.StartEffect);
                while (_effectBuffer.Count > 0)
                    onPlayEffect(_effectBuffer.Dequeue(), skipTime);
            }

            if (onPlaySound != null)
                onPlaySound(animData.SoundID);
        }

        public void RemoveStartEffect(Tab_Animation animData)
        {
            if (onRemoveEffect != null)
            {
                PushEffectsToBuffer(animData.StartEffect);
                while (_effectBuffer.Count > 0)
                    onRemoveEffect(_effectBuffer.Dequeue());
            }

            if (onStopSound != null)
                onStopSound(animData.SoundID);
        }

        private static void PushEffectsToBuffer(string animText)
        {
            var animTexts = animText.Split(';');
            for (var i = 0; i < animTexts.Length; i++)
            {
                int temp;
                if (int.TryParse(animTexts[i], out temp) && temp >= 0)
                    _effectBuffer.Enqueue(temp);
            }
        }

        private void ConsumeInAnimator()
        {
            if (_inAnimator != null)
            {
                RemoveStartEffect(_inAnimator);
                PlayEndEffect(_inAnimator);
                _inAnimator = null;
            }
        }

        public void PlayEndEffect(Tab_Animation animData)
        {
            if (onPlayEffect != null)
            {
                PushEffectsToBuffer(animData.EndEffect);
                while (_effectBuffer.Count > 0)
                    onPlayEffect(_effectBuffer.Dequeue(), 0f);
            }
        }

        /// <summary>
        ///     检查当前播放的动画是否为需要动画
        /// </summary>
        public bool IsPlayAnim(int animId)
        {
            var animData = TableManager.GetAnimationByID(animId, 0);
            return IsPlayAnim(animData);
        }

        /// <summary>
        ///     检查当前播放的动画是否为需要动画
        /// </summary>
        public bool IsPlayAnim(Tab_Animation animData)
        {
            var result = false;
            if (AnimPlayable() && animData != null)
            {
                var nameHash = GetNameHash(animData);
                result = nameHash ==
                         _animationControl.GetCurrentAnimatorStateInfo(0).shortNameHash;
            }

            return result;
        }

        public bool AnimPlayable()
        {
            return _animationControl != null &&
                _animationControl.runtimeAnimatorController != null;
        }

        // 防止一帧收到多个Play指令,导致Animator抽风,动画统一在LateUpdate更新
        public void Play(int animId, string transitionName = default(string))
        {
            if (animId > -1)
            {
                var animData = GetAnimInfoById(animId);
                if (animData != null)
                    Play(animData, transitionName);
            }
        }

        public void Play(Tab_Animation animData, string transitionName = default(string))
        {
            // 不做过多判断 - 直接缓存NextAnim
            if (AnimPlayable() && animData != null)
            {
                _nextInComponent = animData;
                _nextTransitionName = transitionName;
                if (onPlayAnim != null)
                    onPlayAnim(animData, transitionName);
            }
        }

        public bool IsInTransition()
        {
            return AnimPlayable() && _animationControl.IsInTransition(0);
        }

        /// <summary>
        ///     初始动画专用流程,会立刻执行动画转换而不会搜集一帧内的情况
        /// </summary>
        // 注:滥用这个接口,可能导致Animator Trigger无法清空的问题
        // 注:等待到LateUpdate的流程会实际使动画机慢一帧,但是找不到一个介于Update和动画机状态刷新之间的时间点
        // 调整ExecuteOrder同热更新会有后患
        public void PlayStartShow(int animId)
        {
            if (AnimPlayable() && animId > -1)
            {
                var animData = GetAnimInfoById(animId);
                if (animData != null)
                {
                    var transitionName = animData.AnimName.ToLower();
                    if (_animationControl.parameters.FindIndex(a => a.name == transitionName) > -1)
                    {
                        _nextInComponent = null;
                        _toAnimator = animData; //AddAnimLogicData(animData);
                        _animationControl.SetTrigger(transitionName);
                    }
//                    else
//                    {
//                        animData = GetAnimInfoById(0);  //默认Idle
//                        if(animData != null)
//                        {
//                            transitionName = animData.AnimName.ToLower();
//                            if(_animationControl.parameters.FindIndex(a => (a.name) == transitionName) > -1)
//                            {
//                                _nextInComponent = null;
//                                _toAnimator = animData; //AddAnimLogicData(animData);
//                                _animationControl.SetTrigger(transitionName);
//                            }
//                        }
//                    }
                }
            }
        }

        private void LateUpdate()
        {
            // 注:不可见情况下,允许推进动画指令,但是不允许执行
            if (AnimPlayable() && (_visibleRendererCount > 0 || _animationControl.cullingMode != AnimatorCullingMode.CullCompletely))
            {
                if (_nextInComponent != null)
                {
                    var nextHash = GetNameHash(_nextInComponent);
                    // 特殊处理转换到同一动画状态,但是需要不同特效的稀有情况
                    if (_toAnimator != null && GetNameHash(_toAnimator) == nextHash)
                    {
                        _toAnimator = _nextInComponent;
                    }
                    // 特殊检测 - 循环动画不刷新自己;
                    // Hack:循环动画不会因为ExitTime结束,因此_currentAnim必然对应自己
                    else if (_nextInComponent.NextAnimID > -1 ||
                             _inAnimatorHash != GetNameHash(_nextInComponent))
                    {
                        // 特殊处理普攻接普攻,如果有SkipTim,使用衔接切换
                        var transitionName = string.IsNullOrEmpty(_nextTransitionName)
                            ? _nextInComponent.AnimName.ToLower()
                            : _nextTransitionName;
                        // 如果动画机不存在对应的状态,跳过参数设置
                        if (_animationControl.parameters.FindIndex(a => a.name == transitionName) > -1)
                        {
                            _toAnimator = _nextInComponent;
                            _animationControl.SetTrigger(transitionName);
                        }
                    }

                    _nextInComponent = null;
                }
                if (_freezeData != null)
                {
                    if (isFreeze)
                    {
                        if (_freezeData.freezeEnd < Time.time)
						{
                            SetAnimFreeze(false);
							_freezeData = null;
						}
                    }
                    else
                    {
                        if (_freezeData.freezeStart < Time.time)
                            SetAnimFreeze(true);
                    }
                }
            }
        }

        public static Tab_Animation GetAnimInfoById(int animId)
        {
            return CommonUtility.TryGetTable(animId, a => TableManager.GetAnimationByID(a, 0));
        }

        public void StartEffect(int effectId)
        {
            if (onPlayEffect != null)
                onPlayEffect(effectId, 0f);
        }

        public void SetCurrAnimatorEnable(string state, bool isEnable = true)
        {
            if (AnimPlayable())
                _animationControl.SetBool(state, isEnable);
        }

        /// <summary>
        ///     如果当前正播放targetAnimId并且没有下个动画,就切换到Idle动画
        /// </summary>
        /// <param name="targetAnimId">目标动画Id</param>
        /// <param name="idleId">站立状态使用的通用动画</param>
        public void SwitchLoopToIdle(int targetAnimId, int idleId)
        {
            if (AnimPlayable())
            {
                var animData = GetAnimInfoById(targetAnimId);
                if (animData.NextAnimID < 0 && IsPlayAnim(animData))
                    Play(idleId);
            }
        }

        public bool HasMotionState(string state, int layer = 0)
        {
            if (_animationControl == null || _animationControl.runtimeAnimatorController == null)
                return false;
            return _animationControl.HasState(layer, Animator.StringToHash(state.ToLower()));
        }

        public static string GetTransitionName(int fromId, int toId)
        {
            var fromAnim = GetAnimInfoById(fromId);
            var toAnim = GetAnimInfoById(toId);
            if (fromAnim != null && toAnim != null)
                return GetTransitionName(fromAnim, toAnim);
            return string.Empty;
        }

        public static string GetTransitionName(Tab_Animation from, Tab_Animation to)
        {
            return from.AnimName.ToLower() + "To" + to.AnimName.ToLower();
        }

        private static int GetNameHash(Tab_Animation data)
        {
            // 注:测试发现,使用StringToHash比查表速度更快。因此不搞StringToHash表。
            return Animator.StringToHash(data.AnimName.ToLower());
        }

        public class AnimationFreezeData
        {
            public readonly float freezeStart;
            public readonly float freezeEnd;

            public AnimationFreezeData(float freezeStart, float freezeEnd)
            {
                this.freezeStart = freezeStart;
                this.freezeEnd = freezeEnd;
            }
        }

        private class BoneData
        {
            public readonly Transform bone;
            public readonly Vector3 position;
            public readonly Quaternion rotation;
            public readonly Vector3 scale;

            public BoneData(Transform root)
            {
                bone = root;
                position = bone.localPosition;
                rotation = bone.localRotation;
                scale = bone.localScale;
            }

            public void Reset()
            {
                bone.localPosition = position;
                bone.localRotation = rotation;
                bone.localScale = scale;
            }
        }
    }
}