using System;
using UnityEngine.Scripting.APIUpdating;
using UnityEngine.Serialization;
namespace UnityEngine.U2D.IK
{
    /// 
    /// Class for storing data for a 2D IK Chain.
    /// 
    [MovedFrom("UnityEngine.Experimental.U2D.IK")]
    [Serializable]
    public class IKChain2D
    {
        [SerializeField]
        [FormerlySerializedAs("m_Target")]
        Transform m_EffectorTransform;
        [SerializeField]
        [FormerlySerializedAs("m_Effector")]
        Transform m_TargetTransform;
        [SerializeField]
        int m_TransformCount;
        [SerializeField]
        Transform[] m_Transforms;
        [SerializeField]
        Quaternion[] m_DefaultLocalRotations;
        [SerializeField]
        Quaternion[] m_StoredLocalRotations;
        /// 
        /// Lengths of IK Chain.
        /// 
        /// Array of lengths in the IK Chain.
        protected float[] m_Lengths;
        /// 
        /// Get Set the Unity Transform used as IK Effector.
        /// 
        public Transform effector
        {
            get => m_EffectorTransform;
            set => m_EffectorTransform = value;
        }
        /// 
        /// Get Set the Unity Transform used as IK Target.
        /// 
        public Transform target
        {
            get => m_TargetTransform;
            set => m_TargetTransform = value;
        }
        /// 
        /// Get the Unity Transforms that are in the IK Chain.
        /// 
        public Transform[] transforms => m_Transforms;
        /// 
        /// Get the root Unity Transform for the IK Chain.
        /// 
        public Transform rootTransform
        {
            get
            {
                if (m_Transforms != null && transformCount > 0 && m_Transforms.Length == transformCount)
                    return m_Transforms[0];
                return null;
            }
        }
        Transform lastTransform
        {
            get
            {
                if (m_Transforms != null && transformCount > 0 && m_Transforms.Length == transformCount)
                    return m_Transforms[transformCount - 1];
                return null;
            }
        }
        /// 
        /// Get and Set the number of Unity Transforms in the IK Chain.
        /// 
        public int transformCount
        {
            get => m_TransformCount;
            set => m_TransformCount = Mathf.Max(0, value);
        }
        /// 
        /// Returns true if the IK Chain is valid. False otherwise.
        /// 
        public bool isValid => Validate();
        /// 
        /// Gets the length of the IK Chain.
        /// 
        public float[] lengths
        {
            get
            {
                if (isValid)
                {
                    PrepareLengths();
                    return m_Lengths;
                }
                return null;
            }
        }
        bool Validate()
        {
            if (effector == null)
                return false;
            if (transformCount == 0)
                return false;
            if (m_Transforms == null || m_Transforms.Length != transformCount)
                return false;
            if (m_DefaultLocalRotations == null || m_DefaultLocalRotations.Length != transformCount)
                return false;
            if (m_StoredLocalRotations == null || m_StoredLocalRotations.Length != transformCount)
                return false;
            if (rootTransform == null)
                return false;
            if (lastTransform != effector)
                return false;
            return !target || !IKUtility.IsDescendentOf(target, rootTransform);
        }
        /// 
        /// Initialize the IK Chain.
        /// 
        public void Initialize()
        {
            if (effector == null || transformCount == 0 || IKUtility.GetAncestorCount(effector) < transformCount - 1)
                return;
            m_Transforms = new Transform[transformCount];
            m_DefaultLocalRotations = new Quaternion[transformCount];
            m_StoredLocalRotations = new Quaternion[transformCount];
            var currentTransform = effector;
            var index = transformCount - 1;
            while (currentTransform && index >= 0)
            {
                m_Transforms[index] = currentTransform;
                m_DefaultLocalRotations[index] = currentTransform.localRotation;
                currentTransform = currentTransform.parent;
                --index;
            }
        }
        void PrepareLengths()
        {
            var currentTransform = effector;
            var index = transformCount - 1;
            if (m_Lengths == null || m_Lengths.Length != transformCount - 1)
                m_Lengths = new float[transformCount - 1];
            while (currentTransform && index >= 0)
            {
                if (currentTransform.parent && index > 0)
                    m_Lengths[index - 1] = (currentTransform.position - currentTransform.parent.position).magnitude;
                currentTransform = currentTransform.parent;
                --index;
            }
        }
        /// 
        /// Restores IK Chain to it's default pose.
        /// 
        /// True to constrain the target rotation. False otherwise.
        public void RestoreDefaultPose(bool targetRotationIsConstrained)
        {
            var count = targetRotationIsConstrained ? transformCount : transformCount - 1;
            for (var i = 0; i < count; ++i)
                m_Transforms[i].localRotation = m_DefaultLocalRotations[i];
        }
        /// 
        /// Explicitly stores the local rotation
        /// 
        public void StoreLocalRotations()
        {
            for (var i = 0; i < m_Transforms.Length; ++i)
                m_StoredLocalRotations[i] = m_Transforms[i].localRotation;
        }
        /// 
        /// Blend between Forward Kinematics and Inverse Kinematics.
        /// 
        /// Weight for blend
        /// True to constrain target rotation. False otherwise.
        public void BlendFkToIk(float finalWeight, bool targetRotationIsConstrained)
        {
            var count = targetRotationIsConstrained ? transformCount : transformCount - 1;
            for (var i = 0; i < count; ++i)
                m_Transforms[i].localRotation = Quaternion.Slerp(m_StoredLocalRotations[i], m_Transforms[i].localRotation, finalWeight);
        }
    }
}