using JetBrains.Annotations;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Movement : MonoBehaviour
{

    public float movementSpeed, jumpVelocity;
    float gravity;
    public float groundGravity, bubbleGravity; // bubble gravity used when bouncing from bubble to bubble

    public Vector3 velocity;
    public float maxGroundSpeed, groundSlowdownSpeed; // groundSlowdownSpeed is a value in [0, 1]
    public float airStrafeAngleDelta;
    public Transform playerTransform;
    public Transform cameraTransform;

    public BubbleManager bubbleManager;
    public float bubbleThreshold;

    public CustomHorizCollider customHorizCollider;
    public CustomVertCollider customVertCollider;
    public float collisionDetectionAngle;

    public bool onGround;


    public float bubbleJumpVelocity;
    public float dashSpeed;
    public float timeSinceLastDash, dashCooldown;


    public float bubbleSpeedMultiplierCoefficient;
    public int bubbleJumpCombo;

    public Rigidbody rb;

    public AudioSource jumpAudioSource;
    public AudioSource boingAudioSource;

    public AudioSource walkAudioSource;
    

    
    
    void onGroundUpdate()
    {
        // temporary setup while there is no collision detection;
        if (transform.position.y <= 0)
        {
            onGround = true;
        }
        else
        {
            onGround = false;
        }
    }
    
    
    float getBubbleSpeedMultiplier()
    {
        return 1 + (bubbleJumpCombo * bubbleSpeedMultiplierCoefficient);
    }


    public bool launchQueued;
    public Vector3 launchVector;

    bool wasLaunched;
    public float launchedAirControl;
    public void launchInDir(Vector3 v)
    {
        launchVector = v;
        launchQueued = true;
        wasLaunched = true;
    }

    void velocityUpdate(Vector3 moveDir, float yInput, float dashInput) // y component reserve to jump
    {
        /*
        Vector3 fwd = playerTransform.forward * input.z;
        fwd.y = 0;
        Vector3 sdw = playerTransform.right * input.x;
        sdw.y = 0;
        

        Vector3 dir = fwd + sdw;
        dir = dir.normalized;
        */

        float comboMovementSpeed = movementSpeed * getBubbleSpeedMultiplier(); 
        Debug.Log("comboMovementSpeed: " + comboMovementSpeed);

        moveDir = moveDir.normalized;


        bool bubbleJump = false;
        float bubbleForceMultipler = 0.0f;

        
        
        if (!onGround) // if currently in the air...
        {
            bubbleForceMultipler = bubbleManager.isInBubble(transform.position, bubbleThreshold);
            if (bubbleForceMultipler >= 0) // if inside of a bubble
            {
                // set on ground to true & force a jump:
                bubbleJump = true;
                yInput = 1;
                
                gravity = bubbleGravity;
            }
        }


        if (onGround)
        {
            if (!launchQueued)
            {
                wasLaunched = false;
            }

            bubbleJumpCombo = 0;

            gravity = groundGravity;

            if (velocity.magnitude > maxGroundSpeed)
            {
                velocity -= velocity * groundSlowdownSpeed * Time.deltaTime;
            }

            velocity = moveDir * movementSpeed;

            if (yInput > 0 || launchQueued) // if jump key pressed
            {
                if (launchQueued)
                {
                    velocity = launchVector;
                    launchQueued = false;
                }
                else
                {
                    velocity.y = jumpVelocity;
                    if (!jumpAudioSource.isPlaying)
                    {
                        jumpAudioSource.time = 0.1f;
                        jumpAudioSource.Play();
                    }
                }
            }


        }
        else if (bubbleJump)
        {
            if (launchQueued)
            {
                velocity = launchVector;
                launchQueued = false;
            }
            else
            {
                bubbleJumpCombo += 1;

                timeSinceLastDash = dashCooldown + 1; // reset dash on succesful bubble jump!

                velocity.y = 0; // zero out velocity

                velocity += moveDir * comboMovementSpeed * Time.deltaTime;

                float angleDelta = wasLaunched ? launchedAirControl : airStrafeAngleDelta;

                velocity = Vector3.RotateTowards(velocity, moveDir, angleDelta, 0.0f); // rotate horizontaly 

                velocity.y = bubbleJumpVelocity;

                boingAudioSource.Play();
                boingAudioSource.time = 1.0f;
            }



        }
        else
        {
            if (launchQueued)
            {
                velocity = launchVector;
                launchQueued = false;
            }
            else
            {
                float yVel = velocity.y; // temporarily store velocity seperately
                velocity.y = 0; // zero out velocity

                float angleDelta = wasLaunched ? launchedAirControl : airStrafeAngleDelta;
                velocity = Vector3.RotateTowards(velocity, moveDir, angleDelta, 0.0f); // rotate horizontaly 
                velocity.y = yVel; // reapply y velocity


                velocity += Vector3.down * gravity * Time.deltaTime;
            }



        }



        dashUpdate(moveDir, dashInput);
    }

    void dashUpdate(Vector3 moveDir, float dashInput)
    {
        Vector3 dashDir = cameraTransform.forward;

        // dashing:
        if (dashInput > 0 && timeSinceLastDash >= dashCooldown && !collisionStatusCheck() && gravity == bubbleGravity) // last check is an attempt to make sure the player is puzzling with bubbles
        {
            velocity += dashDir * dashSpeed;

            timeSinceLastDash = 0;
        }

        timeSinceLastDash += Time.deltaTime;
    }

    Vector3 handleHorizontalCollisions(Vector3 moveDir) // returns resulting moveDir (as input needs to be modified to only take components perpendicular)
    {
        Vector3 output = moveDir;

        if (Vector3.Angle(customHorizCollider.horizResolveVector, moveDir) < collisionDetectionAngle && customHorizCollider.horizResolveVector != Vector3.zero)
        {
            Debug.Log("handling collision");
            customHorizCollider.horizResolveVector.y = 0;

            output = moveDir - customHorizCollider.horizResolveVector.normalized;
            Debug.Log("collision handled moveDir: " + output);
            float yVel = velocity.y; // store y vel before reflection
            velocity.y = 0;

            velocity = Vector3.Reflect(velocity, customHorizCollider.horizResolveVector.normalized);
            Debug.Log("collision handled velocity: " + velocity);

            velocity.y = yVel;
        }

        return output;
    }


    public float collisionReflectionAngleThreshold;
    Vector3 handleVerticalCollisions(Vector3 moveDir)
    {
        Vector3 output = moveDir;

        if (customVertCollider.vertResolveVector != Vector3.zero)
        {
            onGround = true;
        }
        else
        {
            onGround = false;
        }

        return output;
    }

    bool collisionStatusCheck() // returns true if a collision is currently present
    {
        return (customHorizCollider.horizResolveVector != Vector3.zero || customHorizCollider.horizResolveVector != Vector3.zero);
    }


    public CharacterController characterController; // this ends up being far easier and more consistent

    void positionUpdate()
    {
        //transform.position += velocity * Time.deltaTime;
        characterController.Move(velocity * Time.deltaTime);

        /*
        //rb.velocity = Vector3.zero;
        
        //rb.velocity = Vector3.zero;
        rb.velocity = velocity;
        rb.freezeRotation = true;
        //transform.position = rb.position;
        */
        
    }

    private void Start()
    {
        velocity = Vector3.zero;
        gravity = groundGravity;
        
    }

    void Update()
    {
        float jumpInput = Input.GetKey(KeyCode.Space) ? 1 : 0;
        float dashInput = Input.GetKey(KeyCode.LeftShift) ? 1 : 0;
        Vector3 input = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical"));

        if (input != Vector3.zero && onGround)
        {
            if (!walkAudioSource.isPlaying)
            {
                walkAudioSource.Play();
                walkAudioSource.time = 0.3f;
            }

            if (walkAudioSource.time >= 0.45f)
            {
                walkAudioSource.Stop();
            }
        }

        input = input.normalized;

        Vector3 moveDir = input.x * playerTransform.right + input.z * playerTransform.forward;
        //moveDir = handleHorizontalCollisions(moveDir);
        handleVerticalCollisions(moveDir);
        
        


        //onGroundUpdate();
        velocityUpdate(moveDir, jumpInput, dashInput);


        positionUpdate();


        if (jumpAudioSource.time > 0.5f)
        {
            jumpAudioSource.Stop();
        }
    }


    /*
    private void FixedUpdate()
    {
        positionUpdate();
    }
    */
}
