using UnityEngine;
using UnityEngine.InputSystem;

public class CharacterMovement : MonoBehaviour
{
[SerializeField]
Rigidbody rb;
[SerializeField]
InputActionAsset controls;
[SerializeField]
InputAction move;
[Header("Debug")]
[SerializeField]
float currentVelocity;
[SerializeField]
float horizontalVelocity;
float deltaTimeOffset = 100f;
[Header("Movement")]
[SerializeField]
Vector2 movementDirection;
[SerializeField]
float speed;
[SerializeField]
float acceleration;
Vector3 counterDir;
[SerializeField]
bool grounded;
[SerializeField]
bool onSlope;
[SerializeField]
float maxSlopeAngle;
[SerializeField]
float slopeAngle;
[SerializeField]
Vector3 slopePlane;
[SerializeField]
float touchSlopeAngle;
float slopeSlideMultiplier = 0;

void Awake()
{
    rb = gameObject.GetComponent<Rigidbody>();
    move = controls.FindActionMap("Player").FindAction("Move");
}

void Update()
{
    currentVelocity = rb.linearVelocity.magnitude;
    horizontalVelocity = Vector3.Magnitude(new Vector3(rb.linearVelocity.x, 0, rb.linearVelocity.z));
    InputReader();
}

void FixedUpdate()
{
    if (!onSlope)
    {
        rb.AddForce(Vector3.down * Time.deltaTime * 800f, ForceMode.Acceleration);
    }
    if (touchSlopeAngle > maxSlopeAngle)
    {
        rb.AddForce(Vector3.down * 4000f * Time.deltaTime, ForceMode.Acceleration);
    }
    if (touchSlopeAngle > maxSlopeAngle && touchSlopeAngle < 87f)
    {
        slopeSlideMultiplier = 0.25f;
    }
    else
    {
        slopeSlideMultiplier = 1f;
    }
    HorizontalMovement();
    CounterMovement();
    SlopeMovement();
}

void InputReader()
{
    movementDirection = move.ReadValue<Vector2>();
}

void HorizontalMovement()
{
    Vector3 moveDir = SlopeMovement();
    rb.AddForce(moveDir.normalized * speed * acceleration * Time.deltaTime * deltaTimeOffset * slopeSlideMultiplier, ForceMode.Acceleration);
    if (horizontalVelocity <= 0.001f)
    {
        rb.linearVelocity = new Vector3(0, rb.linearVelocity.y, 0);
    }
}

void CounterMovement()
{
    if (movementDirection.magnitude <= 0.001f && horizontalVelocity <= 0.001f)
    {
        return;
    }
    if (onSlope || grounded)
    {
        counterDir = rb.linearVelocity;
    }
    else
    {
        counterDir = new Vector3(rb.linearVelocity.x, 0, rb.linearVelocity.z);
    }
    rb.AddForce(counterDir * -acceleration * Time.deltaTime * deltaTimeOffset, ForceMode.Acceleration);
}

Vector3 SlopeMovement()
{
    Vector3 wishDir = transform.forward * movementDirection.y + transform.right * movementDirection.x;
    bool slopeCheck = Physics.Raycast(transform.position, Vector3.down, out RaycastHit hit, 1.5f);
    slopeAngle = Vector3.Angle(Vector3.up, slopePlane);
    slopePlane = hit.normal;
    if (slopeAngle > maxSlopeAngle || slopeAngle <= 0.001f)
    {
        onSlope = false;
    }
    else
    {
        onSlope = slopeCheck;
    }
    if (!onSlope)
    {
        rb.useGravity = true;
        return wishDir;
    }
    if (rb.linearVelocity.y > 0)
    {
        rb.AddForce(Vector3.down * 800f * Time.deltaTime, ForceMode.Acceleration);
    }
    rb.useGravity = false;
    return Vector3.ProjectOnPlane(wishDir, slopePlane).normalized;
}

void OnCollisionStay(Collision collision)
{
    for (int contactpoint = 0; contactpoint < collision.contactCount; contactpoint++)
    {
        Vector3 touchSlope = collision.GetContact(contactpoint).normal;
        touchSlopeAngle = Vector3.Angle(Vector3.up, touchSlope);
        if (Vector3.Dot(touchSlope.normalized, Vector3.up) >= 0.975f)
        {
            grounded = true;
        }
        else
        {
            grounded = false;
        }
    }
}

void OnCollisionExit(Collision collision)
{
    touchSlopeAngle = 0;
    grounded = false;
    onSlope = false;
}

}