using Assets.Scripts;
using System.Collections;
using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerMovement : MonoBehaviour
{
    private float horizontal;
    public float speed = 7f;
    private bool isFacingRight = true;

    private float sanity = 100.0f;

    [SerializeField] private Rigidbody2D rb;
    [SerializeField] private SpriteRenderer sr;
    [SerializeField] private Transform groundCheck;
    [SerializeField] private LayerMask ShipPartsLayer;

    private RaycastHit2D leftHitInfo = default;
    private RaycastHit2D rightHitInfo = default;

    private RayProperties horizontalRayProperties;

    private float rayLenght = 0.4f;
    private TerrainMovementRayProperties movementRay;
    private float maxRotationDegrees;
    private float positionOffsetY;

    private bool isMoving;
    private float falling = 0f;
    private float heighttoground = 0;

    // Start is called before the first frame update
    void Start()
    {
        horizontalRayProperties = new();
        horizontalRayProperties.attachedRayLength = 0.02f;
        horizontalRayProperties.originOffsetY = -0.06f;

        movementRay = new();
        movementRay.originOffsetY = 0.04f;
        movementRay.distanceFromCenter = 0.1f;

        maxRotationDegrees = 5;
        positionOffsetY = 0.1f;
    }

    public void Move(
        InputValue movementValue
    )
    {
        Vector2 movementVector = movementValue.Get<Vector2>();
        horizontal = movementVector.x;
    }



    // Update is called once per frame
    void Update()
    {
        Flip();
    }

    private void FixedUpdate()
    {
        //CheckifFolling();

        CalculateVelocity();

        if (doubleRaycastDown(movementRay, rayLenght, out leftHitInfo, out rightHitInfo))
        {
            AntiClipGeometryMeasue();

            positionOnTerrain(leftHitInfo, rightHitInfo, rayLenght, maxRotationDegrees, positionOffsetY, isGrounded());
        }
    }

    private bool isGrounded()
    {
        return Physics2D.OverlapCircle(groundCheck.position, 0.1f, ShipPartsLayer);
    }

    private void Flip()
    {
        if (isFacingRight && horizontal < 0f ||
            !isFacingRight && horizontal > 0f)
        {
            isFacingRight = !isFacingRight;
            Vector3 localScale = transform.localScale;
            localScale.x *= -1f;
            transform.localScale = localScale;
        }
    }

    public IEnumerator Falling()
    {
        float nearestDistance = 999999999.0f;
        GameObject nearestObject = gameObject;
        falling = 2f;

        while (!isGrounded())
        {
            var overlappingColliders = Physics2D.OverlapCircleAll(transform.position, 20, ShipPartsLayer);

            foreach (Collider2D collider in overlappingColliders)
            {
                if (collider.gameObject != gameObject)
                {
                    float distance = Vector2.Distance(transform.position, collider.transform.position);

                    if (distance < nearestDistance)
                    {
                        nearestObject = collider.gameObject;
                        nearestDistance = distance;
                    }
                }
            }

            // Calculate the direction towards the target
            Vector3 direction = nearestObject.transform.position - transform.position;

            Quaternion rotationQuaternion = Quaternion.AngleAxis(180, Vector3.forward);
            Vector3 rotatedVector = rotationQuaternion * direction;

            // Normalize the direction
            rotatedVector = rotatedVector.normalized;

            // Calculate the rotation angle
            Quaternion targetRotation = Quaternion.FromToRotation(Vector3.up, rotatedVector);
            Quaternion finalRotation = Quaternion.RotateTowards(transform.rotation, targetRotation, maxRotationDegrees);

            transform.rotation = Quaternion.Euler(0, 0, finalRotation.eulerAngles.z);

            yield return null;
        }
        falling = 0f;
    }

    private void CheckifFolling()
    {
        if (!isGrounded())
        {
            falling = 1f;

            Ray2D fallingRay = new Ray2D(transform.position, transform.up * -1);

            RaycastHit2D xxx = Physics2D.Raycast(
                fallingRay.origin,
                fallingRay.direction,
                20f,
                ShipPartsLayer
            );

            heighttoground = xxx.distance;

            Debug.DrawRay(fallingRay.origin, fallingRay.direction * heighttoground, new Color(1f, 0f, 0f), 1); // red
        }
        else
        {
            falling = 0f;
            heighttoground = 0.5f;
        }
    }

    private void CalculateVelocity()
    {
        Vector2 tempvec = new Vector2(horizontal * speed, 0);
        rb.velocity = transform.right * tempvec.magnitude * Mathf.Sign(horizontal) + transform.up * -falling;
    }

    private void AntiClipGeometryMeasue()
    {
        if (rb.velocity.sqrMagnitude > 0)
        {
            //if moving left
            if (MathUtilities.VectorSimilarity(rb.velocity, transform.right) < 0)
            {
                Ray2D overrideLeftRay = new Ray2D(
                    transform.position + horizontalRayProperties.originOffsetY * transform.up,
                    transform.right);

                Debug.DrawRay(overrideLeftRay.origin, overrideLeftRay.direction * horizontalRayProperties.attachedRayLength, new Color(1f, 1f, 1f), 1); // white

                RaycastHit2D overrideLeftHitInfo = Physics2D.Raycast(
                    overrideLeftRay.origin,
                    overrideLeftRay.direction,
                    horizontalRayProperties.attachedRayLength,
                    ShipPartsLayer
                    );

                if (overrideLeftHitInfo != null &&
                    overrideLeftHitInfo.collider != null)
                {
                    Debug.Log("zmiana leftHitInfo");
                    leftHitInfo = overrideLeftHitInfo;
                }

            }
            //if moving right
            else
            {
                Ray2D overrideRightRay = new Ray2D(
                    transform.position + horizontalRayProperties.originOffsetY * transform.up,
                    -transform.right);

                Debug.DrawRay(overrideRightRay.origin, overrideRightRay.direction * horizontalRayProperties.attachedRayLength, new Color(0.5f, 0.5f, 0.5f), 1); // grey


                RaycastHit2D overrideRightHitInfo = Physics2D.Raycast(
                    overrideRightRay.origin,
                    overrideRightRay.direction,
                    horizontalRayProperties.attachedRayLength,
                    ShipPartsLayer
                    );

                if (overrideRightHitInfo != null &&
                    overrideRightHitInfo.collider != null)
                {
                    Debug.Log("zmiana rightHitInfo");
                    rightHitInfo = overrideRightHitInfo;
                }

            }
        }
    }


    bool doubleRaycastDown(

        TerrainMovementRayProperties movementRay,
        float rayLength,
        out RaycastHit2D leftHitInfo,
        out RaycastHit2D rightHitInfo
    )
    {
        Vector3 transformUp = transform.up;
        Vector3 transformRight = transform.right;

        leftHitInfo = default;
        rightHitInfo = default;


        Ray2D leftRay = new Ray2D(
            transform.position + movementRay.originOffsetY * transformUp
            + movementRay.distanceFromCenter * transformRight,
            -transformUp);

        Ray2D rightRay = new Ray2D(
            transform.position + movementRay.originOffsetY * transformUp
            - movementRay.distanceFromCenter * transformRight,
            -transformUp);

        Debug.DrawRay(leftRay.origin, leftRay.direction * rayLength, new Color(1f, 0f, 0f), 1); // red
        Debug.DrawRay(rightRay.origin, rightRay.direction * rayLength, new Color(1f, 1f, 0f), 1); // yellow

        RaycastHit2D x1 = Physics2D.Raycast(
                leftRay.origin,
                leftRay.direction,
                rayLength,
                ShipPartsLayer
            );

        RaycastHit2D x2 = Physics2D.Raycast(
                rightRay.origin,
                rightRay.direction,
                rayLength,
                ShipPartsLayer
            );

        leftHitInfo = x1;
        rightHitInfo = x2;

        return
            Physics2D.Raycast(
                leftRay.origin,
                leftRay.direction,
                rayLength,
                ShipPartsLayer
            )
            &&
            Physics2D.Raycast(
                rightRay.origin,
                rightRay.direction,
                rayLength,
                ShipPartsLayer
            );
    }

    void positionOnTerrain(
        RaycastHit2D leftHitInfo,
        RaycastHit2D rightHitInfo,
        float rayLength,
        float maxRotationDegrees,
        float positionOffsetY,
        bool isgrounded)
    {
        if (isgrounded)
        {
            Vector3 averageNormal = (leftHitInfo.normal + rightHitInfo.normal) / 2;
            Vector3 averagePoint = (leftHitInfo.point + rightHitInfo.point) / 2;

            Debug.DrawRay(leftHitInfo.point, leftHitInfo.normal * rayLength, new Color(1f, 0f, 1f), 1); // magenta
            Debug.DrawRay(rightHitInfo.point, rightHitInfo.normal * rayLength, new Color(0f, 1f, 0f), 1); // green
            Debug.DrawRay(averagePoint, averageNormal * rayLength, new Color(0f, 0f, 1f), 1); // blue

            Quaternion targetRotation = Quaternion.FromToRotation(Vector3.up, averageNormal);
            Quaternion finalRotation = Quaternion.RotateTowards(transform.rotation, targetRotation, maxRotationDegrees);

            transform.rotation = Quaternion.Euler(0, 0, finalRotation.eulerAngles.z);


            transform.position = averagePoint + transform.up * (positionOffsetY + heighttoground);
        }

    }

}