using System;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Scripting.APIUpdating;

/// <summary>
/// ��ɫ������
/// </summary>
public class PlayerController : MonoBehaviour
{
    #region ���л�
    public Rigidbody[] m_RigsToLift;
    public ConstantForce m_LeftKneeForce;
    public ConstantForce m_RightKneeForce;
    [Header("Gravity")]
    public Vector3 m_ExtraGravity;
    [Header("TurnBody")]
    public float m_TorqueForce;
    [Header("Stand")]
    public float m_StandHeight;
    public float m_StandUpForceMultiplier;
    public AnimationCurve m_StandUpForceCurve;
    [Header("Move")]
    public float m_HorizontalMoveForce;
    public float m_JumpForce;
    public float m_SwitchLegInterval;
    public float m_LegAngle;
    public float m_TorsoForce;
    [Header("����")]
    public JointAnimation m_MoveAnimation;
    #endregion

    #region �����ֶ�
    [NonSerialized] public CharacterInformation info;

    public Vector3 forward
    {
        get
        {
            return -transform.forward;
        }
    }

    #endregion

    #region ˽���ֶ�
    private Rigidbody[] m_AllRigidBodies;
    private Rigidbody m_HipRigidBody;
    private Rigidbody m_HeadRigidBody;
    private Rigidbody m_TorsoRigidBody;
    private Rigidbody m_LegLeftRigidBody;
    private Rigidbody m_LegRightRigidBody;

    private Quaternion m_TowardDirection; // ����Ƕ�

    private bool m_LastMove;
    private bool m_DoJump;
    private bool m_DoMoveLeft;
    private bool m_DoMoveRight;

    private float m_StepSwitchTimer;
    private bool m_IsLeftForward;

    private float m_FallenGravityFactor;

    #endregion

    private void Awake()
    {
        InitRigidBodies();

        m_HipRigidBody = transform.Find("Rigidbodies/Hip").GetComponent<Rigidbody>();
        m_HeadRigidBody = transform.Find("Rigidbodies/Head").GetComponent<Rigidbody>();
        m_TorsoRigidBody = transform.Find("Rigidbodies/Torso").GetComponent<Rigidbody>();
        m_LegLeftRigidBody = transform.Find("Rigidbodies/Leg_Left").GetComponent<Rigidbody>();
        m_LegRightRigidBody = transform.Find("Rigidbodies/Leg_Right").GetComponent<Rigidbody>();

        info = GetComponent<CharacterInformation>();

        m_TowardDirection = Quaternion.Euler(0, -90, 0);
        m_DoJump = false;
        m_LastMove = false;
    }

    private void Update()
    {
        m_DoMoveLeft = false;
        m_DoMoveRight = false;

        if (Input.GetKey(KeyCode.A))
        {
            m_TowardDirection = Quaternion.Euler(0, -90, 0);
        }
        if (Input.GetKey(KeyCode.D))
        {
            m_TowardDirection = Quaternion.Euler(0, 90, 0);
        }
        if (Input.GetKeyDown(KeyCode.W))
        {
            m_DoJump = true;
        }
        if (Input.GetKey(KeyCode.A))
        {
            m_DoMoveLeft = true;
        }
        if (Input.GetKey(KeyCode.D))
        {
            m_DoMoveRight = true;
        }

        m_StepSwitchTimer += Time.deltaTime;
        if(m_StepSwitchTimer > m_SwitchLegInterval && Vector3.Angle(m_LegLeftRigidBody.transform.up, m_LegRightRigidBody.transform.up) > m_LegAngle)
        {
            m_IsLeftForward = !m_IsLeftForward;
            m_StepSwitchTimer = m_StepSwitchTimer - m_SwitchLegInterval;
        }
    }

    private void FixedUpdate()
    {
        StandUp();
        TurnBody();
        OnMove();
    }

    private void OnMove()
    {
        if (!(m_DoMoveLeft || m_DoMoveRight || m_DoJump))
        {
            m_LastMove = false;
            return;
        }

        //if (info.sinceTurn != 0)
        //{
        //    return;
        //}

        if (!m_LastMove) // enter move
        {
            m_StepSwitchTimer = 0;

            m_LastMove = true;
        }

        m_MoveAnimation.Animate();

        if(m_DoMoveLeft)
        {
            MoveLeft();
        }
        if (m_DoMoveRight)
        {
            MoveRight();
        }
        if (m_DoJump)
        {
            Jump();
            m_DoJump = false;
        }
    }

    void MoveLeft()
    {
        DoActionToAllRigidbodies((rig, i) => {
            rig.AddForce(Vector3.left * m_HorizontalMoveForce *  Time.fixedDeltaTime, ForceMode.Acceleration);
        });
        m_TorsoRigidBody.AddForce(Vector3.left * m_TorsoForce, ForceMode.Force);
    }

    void MoveRight()
    {
        DoActionToAllRigidbodies((rig, i) =>
        {
            rig.AddForce(Vector3.right * m_HorizontalMoveForce * Time.fixedDeltaTime, ForceMode.Acceleration);
        });
        m_TorsoRigidBody.AddForce(Vector3.right * m_TorsoForce, ForceMode.Force);
    }

    void Jump()
    {
        if(info.isGrounded || info.sinceGrounded < 0.2f)
        {
            Debug.Log("Jump");
            DoActionToAllRigidbodies((rig) => {
                rig.AddForce(Vector3.up * m_JumpForce * Time.fixedDeltaTime, ForceMode.VelocityChange);
            });
        }
    }

    private void StandUp()
    {
        if (!info.isGrounded) // air
        {
            m_FallenGravityFactor += Time.fixedDeltaTime;
            //m_FallenGravityFactor = Mathf.Clamp(m_FallenGravityFactor, 0, 1);

            ApplyExtraGravity();
        }
        else // grounded
        {
            m_FallenGravityFactor = 0;

            //Vector3 standUpForce = m_StandUpForce * Vector3.up * (m_StandHeight - info.shortestDistanceFromHeadToGround);
            float headHeight = info.HeadRayCast();
            if(headHeight < m_StandHeight)
            {
                float t = 1 - (m_StandHeight - headHeight) / m_StandHeight;
                t = Mathf.Clamp(t, 0, 1);
                //Debug.Log(t);
                Vector3 standUpForce = m_StandUpForceMultiplier * m_StandUpForceCurve.Evaluate(t) * Vector3.up;
                for (int i = 0; i < m_RigsToLift.Length; i++)
                {
                    m_RigsToLift[i].AddForce(info.GetTotalMass() * standUpForce + info.GetTotalMass() * -Physics.gravity, ForceMode.Force);
                }
            }
        }

        if(info.sinceGrounded < 0.3f)
        {
            m_LeftKneeForce.enabled = true;
            m_RightKneeForce.enabled = true;
        }
        else
        {
            m_LeftKneeForce.enabled = false;
            m_RightKneeForce.enabled = false;
        }
    }

    private void TurnBody()
    {
        Vector3 properForward = m_TowardDirection * -Vector3.forward;
        float angel = Vector3.Angle(m_HipRigidBody.transform.forward, properForward);
        Vector3 dir = Vector3.Cross(m_HipRigidBody.transform.forward, properForward);
        if(Mathf.Abs(angel) < 5f)
        {
            info.sinceTurn = 0;
        }
        if(dir.y > 0)
        {
            m_HipRigidBody.AddTorque(Vector3.up * angel * m_TorqueForce, ForceMode.Acceleration) ;
        }
        else if(dir.y < 0)
        {
            m_HipRigidBody.AddTorque(-Vector3.up * angel * m_TorqueForce, ForceMode.Acceleration);
        }
    }

    /// <summary>
    /// ��ʼ��rigidbody
    /// </summary>
    private void InitRigidBodies() //��ʼ�����壬���
    {
        this.m_AllRigidBodies = base.GetComponentsInChildren<Rigidbody>();
        DoActionToAllRigidbodies((rig, i) => { 
        });

        IgnoreCollision();
    }

    /// <summary>
    /// �������������֮�����ײ 
    /// </summary>
    public void IgnoreCollision()
    {
        for (int i = 0; i < m_AllRigidBodies.Length - 1; i++)
        {
            for (int j = i + 1; j < m_AllRigidBodies.Length; j++)
            {
                Collider collider1 = m_AllRigidBodies[i].GetComponent<Collider>();
                Collider collider2 = m_AllRigidBodies[j].GetComponent<Collider>();
                Physics.IgnoreCollision(collider1, collider2, true);
            }
        }
    }

    private void ApplyExtraGravity()
    {
        DoActionToAllRigidbodies((rig) => {
            rig.AddForce(m_ExtraGravity * m_FallenGravityFactor, ForceMode.Acceleration);
        });
    }

    private void DoActionToAllRigidbodies(Action<Rigidbody> action)
    {
        DoActionToAllRigidbodies((rig, i) => {
            action(rig);
        });
    }

    private void DoActionToAllRigidbodies(Action<Rigidbody, int> action)
    {
        for (int i = 0; i < m_AllRigidBodies.Length; ++i)
        {
            if (action != null)
            {
                action(m_AllRigidBodies[i], i);
            }
        }
    }

    public bool StepLeft()
    {
        return m_IsLeftForward;
    }

    public bool StepRight()
    {
        return !m_IsLeftForward;
    }

}