using UnityEngine;
using System.Collections;
using Valve.VR.InteractionSystem;

namespace Valve.VR.InteractionSystem.Sample
{
    public class JoeJeff : MonoBehaviour
    {
        public float animationSpeed;

        public float jumpVelocity;

        [SerializeField]
        private float m_MovingTurnSpeed = 360;
        [SerializeField]
        private float m_StationaryTurnSpeed = 180;

        public float airControl;

        [Tooltip("The time it takes after landing a jump to slow down")]
        public float frictionTime = 0.2f;

        [SerializeField]
        private float footHeight = 0.1f;
        [SerializeField]
        private float footRadius = 0.03f;

        private RaycastHit footHit;

        private bool isGrounded;

        private float turnAmount;
        private float forwardAmount;

        private float groundedTime;

        private Animator animator;

        private Vector3 input;

        private bool held;

        private new Rigidbody rigidbody;
        private Interactable interactable;

        public FireSource fire;


        private void Start()
        {
            animator = GetComponent<Animator>();
            rigidbody = GetComponent<Rigidbody>();
            interactable = GetComponent<Interactable>();
            animator.speed = animationSpeed;
        }

        private void Update()
        {
            held = interactable.attachedToHand != null;

            jumpTimer -= Time.deltaTime;

            CheckGrounded();

            rigidbody.freezeRotation = !held;

            if (held == false)
                FixRotation();
        }

        private void FixRotation()
        {
            Vector3 eulers = transform.eulerAngles;
            eulers.x = 0;
            eulers.z = 0;
            Quaternion targetRotation = Quaternion.Euler(eulers);

            transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * (isGrounded ? 20 : 3));
        }


        public void OnAnimatorMove()
        {
            // we implement this function to override the default root motion.
            // this allows us to modify the positional speed before it's applied.
            if (Time.deltaTime > 0)
            {
                Vector3 animationDelta = (animator.deltaPosition) / Time.deltaTime;

                animationDelta = Vector3.ProjectOnPlane(animationDelta, footHit.normal);

                if (isGrounded && jumpTimer < 0)
                {
                    if (groundedTime < frictionTime) //slow down when first hitting the floor after a jump
                    {
                        float moveFac = Mathf.InverseLerp(0, frictionTime, groundedTime);
                        //print(moveFac);
                        Vector3 lerpV = Vector3.Lerp(rigidbody.velocity, animationDelta, moveFac * Time.deltaTime * 30);
                        animationDelta.x = lerpV.x;
                        animationDelta.z = lerpV.z;
                    }

                    // adding a little downward force to keep him on the floor
                    animationDelta.y += -0.2f;// rb.velocity.y;
                    rigidbody.velocity = animationDelta;
                }
                else
                {
                    rigidbody.velocity += input * Time.deltaTime * airControl;
                }
            }
        }

        public void Move(Vector3 move, bool jump)
        {
            input = move;
            if (move.magnitude > 1f)
                move.Normalize();

            move = transform.InverseTransformDirection(move);

            turnAmount = Mathf.Atan2(move.x, move.z);
            forwardAmount = move.z;

            ApplyExtraTurnRotation();

            // control and velocity handling is different when grounded and airborne:
            if (isGrounded)
            {
                HandleGroundedMovement(jump);
            }


            // send input and other state parameters to the animator
            UpdateAnimator(move);
        }

        private void UpdateAnimator(Vector3 move)
        {
            animator.speed = fire.isBurning ? animationSpeed * 2 : animationSpeed;
            // update the animator parameters
            animator.SetFloat("Forward", fire.isBurning ? 2 : forwardAmount, 0.1f, Time.deltaTime);
            animator.SetFloat("Turn", turnAmount, 0.1f, Time.deltaTime);
            animator.SetBool("OnGround", isGrounded);
            animator.SetBool("Holding", held);

            if (!isGrounded)
            {
                animator.SetFloat("FallSpeed", Mathf.Abs(rigidbody.velocity.y));
                animator.SetFloat("Jump", rigidbody.velocity.y);
            }
        }

        private void ApplyExtraTurnRotation()
        {
            // help the character turn faster (this is in addition to root rotation in the animation)
            float turnSpeed = Mathf.Lerp(m_StationaryTurnSpeed, m_MovingTurnSpeed, forwardAmount);
            transform.Rotate(0, turnAmount * turnSpeed * Time.deltaTime, 0);
        }

        private void CheckGrounded()
        {
            isGrounded = false;
            if (jumpTimer < 0 & !held) // make sure we didn't just jump
            {
                isGrounded = (Physics.SphereCast(new Ray(transform.position + Vector3.up * footHeight, Vector3.down), footRadius, out footHit, footHeight - footRadius));
                if (Vector2.Distance(new Vector2(transform.position.x, transform.position.z), new Vector2(footHit.point.x, footHit.point.z)) > footRadius / 2)
                {
                    isGrounded = false;
                    //on slope, hit point is on edge of sphere cast
                }
            }
        }


        private void FixedUpdate()
        {
            groundedTime += Time.fixedDeltaTime;
            if (!isGrounded) groundedTime = 0; // reset timer

            if (isGrounded & !held)
            {
                Debug.DrawLine(transform.position, footHit.point);

                rigidbody.position = new Vector3(rigidbody.position.x, footHit.point.y, rigidbody.position.z);
            }
        }



        private void HandleGroundedMovement(bool jump)
        {
            // check whether conditions are right to allow a jump:
            if (jump && isGrounded)
            {
                Jump();
            }
        }

        private float jumpTimer;
        public void Jump()
        {
            isGrounded = false;
            jumpTimer = 0.1f;
            animator.applyRootMotion = false;
            rigidbody.position += Vector3.up * 0.03f;
            Vector3 velocity = rigidbody.velocity;
            velocity.y = jumpVelocity;
            rigidbody.velocity = velocity;
        }
    }
}