using System;
using System.Collections.Generic;
using UnityEngine;

namespace NBF
{
    public class FishingLineNode : MonoBehaviour
    {
        public enum NodeType
        {
            Start,
            Bobber,
            Weight,
            End
        }

        [SerializeField] private FLine _line;
        [SerializeField] private FishingLineNode _preNode;
        [Header("Node")] [SerializeField] private NodeType nodeType = NodeType.End;

        [SerializeField] private Rigidbody body;
        [SerializeField] private ConfigurableJoint joint;
        [SerializeField] private VerletRope rope;
        [SerializeField] private List<FishingLineNodeFeature> features = new();
        [SerializeField] private List<FishingLineNodeMotionFeature> motionFeatures = new();
        private bool featureCacheReady;
        [SerializeField] private FishingLineNodeMotionFeature activeMotionFeature;

        [Header("线极限设置")] [SerializeField] private float normalAdditive = 0.3f;
        [SerializeField] private float limitAdditive = 2f;

        /// <summary>
        /// 当前正在接管节点运动的组件。
        /// </summary>
        public FishingLineNodeMotionFeature ActiveMotionFeature => activeMotionFeature;

        public NodeType Type
        {
            get => nodeType;
            set => nodeType = value;
        }

        public float Lenght { get; private set; }

        /// <summary>
        /// 真实实际长度
        /// </summary>
        public float RealLength { get; private set; }

        /// <summary>
        /// 当前逻辑链总长度超出配置总长度的部分，小于等于零时记为 0。
        /// </summary>
        public float StretchLength { get; private set; }

        public VerletRope Rope => rope;
        public Rigidbody Body => body;
        public ConfigurableJoint Joint => joint;
        public Vector3 Position => transform.position;
        public FishingLineNode PreNode => _preNode;

        private void Awake()
        {
            if (!_line) _line = GetComponentInParent<FLine>();
            TryGetComponent(out body);
            TryGetComponent(out joint);
            body.isKinematic = true;
            EnsureFeatureCache();
        }

        private void Start()
        {
            BindFeatures(_line);
        }

        private void FixedUpdate()
        {
            EnsureFeatureCache();
            UpdateMotionControl(Time.fixedDeltaTime);
            UpdateLenght();
        }


        private void UpdateLenght()
        {
            RealLength = 0;
            StretchLength = 0;
            if (_preNode)
            {
                RealLength = Vector3.Distance(transform.position, _preNode.transform.position);
            }

            if (RealLength > Lenght)
            {
                var len = rope.GetCurrentConstraintLength();
                StretchLength = RealLength - Lenght;
            }
        }

        #region Line

        public void SetRope(VerletRope verletRope)
        {
            rope = verletRope;
            Body.isKinematic = false;
        }

        public void SetLenght(float lenght)
        {
            if (lenght < 0.1f) lenght = 0.1f;
            Lenght = lenght;
            rope.SetRopeLength(lenght);

            SetJointLenght(lenght);
        }

        public void SetJointLenght(float lenght)
        {
            if (!joint) return;
            var jointLenght = lenght + normalAdditive;
            if (_line && _line.IsRopeLimit)
            {
                jointLenght = lenght + limitAdditive;
            }

            if (!Mathf.Approximately(jointLenght, Joint.linearLimit.limit))
            {
                Joint.linearLimit = new SoftJointLimit() { limit = jointLenght };
            }
        }

        #endregion

        #region Feature

        /// <summary>
        /// 获取节点上的第一个指定类型功能组件。
        /// </summary>
        public T GetFeature<T>() where T : FishingLineNodeFeature
        {
            EnsureFeatureCache();
            for (var i = 0; i < features.Count; i++)
            {
                if (features[i] is T result)
                {
                    return result;
                }
            }

            return null;
        }

        /// <summary>
        /// 尝试获取节点上的指定类型功能组件。
        /// </summary>
        public bool TryGetFeature<T>(out T feature) where T : FishingLineNodeFeature
        {
            feature = GetFeature<T>();
            return feature != null;
        }

        /// <summary>
        /// 刷新并重新绑定当前节点上的功能组件。
        /// </summary>
        public void BindFeatures(FLine solver)
        {
            EnsureFeatureCache();
            foreach (var t in features)
            {
                t.Bind(this, solver);
            }

            ResolveMotionFeature(forceRefresh: true);
        }

        /// <summary>
        /// 通知当前节点上的所有功能组件，鱼线已重建完成。
        /// </summary>
        public void NotifyLineBuilt()
        {
            EnsureFeatureCache();
            foreach (var t in features)
            {
                t.OnLineBuilt();
            }

            ResolveMotionFeature(forceRefresh: true);
        }

        /// <summary>
        /// 通知当前节点上的所有功能组件，鱼线已经达到断线条件。
        /// </summary>
        public void NotifyLineBreakRequested()
        {
            EnsureFeatureCache();
            foreach (var t in features)
            {
                t.OnLineBreakRequested();
            }
        }


        private void EnsureFeatureCache()
        {
            if (!featureCacheReady)
            {
                RefreshFeatures();
            }
        }

        private void RefreshFeatures()
        {
            features.Clear();
            motionFeatures.Clear();
            GetComponents(features);
            for (var i = 0; i < features.Count; i++)
            {
                if (features[i] is FishingLineNodeMotionFeature motionFeature)
                {
                    motionFeatures.Add(motionFeature);
                }
            }

            activeMotionFeature = null;
            featureCacheReady = true;
        }

        private void UpdateMotionControl(float deltaTime)
        {
            var motionFeature = ResolveMotionFeature(forceRefresh: false);
            if (motionFeature == null)
            {
                return;
            }

            motionFeature.TickMotion(deltaTime);
        }

        private FishingLineNodeMotionFeature ResolveMotionFeature(bool forceRefresh)
        {
            EnsureFeatureCache();

            var bestMotionFeature = default(FishingLineNodeMotionFeature);
            var bestPriority = int.MinValue;

            foreach (var motionFeature in motionFeatures)
            {
                var r = !motionFeature.IsSupportedNode(this);
                var n = !motionFeature.CanControl();
                if (motionFeature == null || !motionFeature.IsSupportedNode(this) || !motionFeature.CanControl())
                {
                    continue;
                }

                if (bestMotionFeature != null && motionFeature.Priority <= bestPriority)
                {
                    continue;
                }

                bestMotionFeature = motionFeature;
                bestPriority = motionFeature.Priority;
            }

            if (!forceRefresh && ReferenceEquals(activeMotionFeature, bestMotionFeature))
            {
                return activeMotionFeature;
            }

            if (activeMotionFeature != null && !ReferenceEquals(activeMotionFeature, bestMotionFeature))
            {
                activeMotionFeature.OnMotionDeactivated();
            }

            activeMotionFeature = bestMotionFeature;
            if (activeMotionFeature != null)
            {
                activeMotionFeature.OnMotionActivated();
            }

            return activeMotionFeature;
        }

        #endregion

        private void OnDrawGizmos()
        {
        }
    }
}