using System.Collections;
using UnityEngine;

namespace Passer {

    /// <summary>
    /// Utility functions to move transformes in the world.
    /// </summary>
    public class Transportation : MonoBehaviour {        
        public float forwardSpeed = 1;
        public float sidewardSpeed = 1;
        public float rotationalSpeed = 60;

        /// <summary>
        /// Moves the transform along the local Z-axis with the indicated speed
        /// </summary>
        /// Should be called every frame for continuous movement.
        /// <param name="z"></param>
        public virtual void MoveForward(float z) {
            transform.MoveForward(forwardSpeed * z * Time.deltaTime);
        }
        /// <summary>
        /// Moves the transform along the local X-axis with the indicated speed
        /// </summary>
        /// Should be called every frame for continuous movement.
        /// <param name="x"></param>
        public virtual void MoveSideward(float x) {
            transform.MoveSidewards(sidewardSpeed * x * Time.deltaTime);
        }
        /// <summary>
        /// Rotates this transform with the indicated speed around the Y axis.
        /// </summary>
        /// Should be called every frame for continuous rotation.
        /// <param name="angularSpeed">The speed in degrees per second</param>
        public virtual void Rotate(float angularSpeed) {
            transform.Turn(rotationalSpeed * angularSpeed * Time.deltaTime);
        }

        /// <summary>
        /// Quickly moves this transform to the targetPosition
        /// </summary>
        /// This is a coroutine which will move the transform using DashCoroutine
        /// It does not need to be called every frame.
        /// <param name="targetPosition"></param>
        public void Dash(Vector3 targetPosition) {
            StartCoroutine(TransformMovements.DashCoroutine(transform, targetPosition));
        }
        /// <summary>
        /// Teleport this transform to the targetPosition
        /// </summary>
        /// <param name="targetPosition">The target position</param>
        public void Teleport(Vector3 targetPosition) {
            TransformMovements.Teleport(this.transform, targetPosition);
        }

        /// <summary>
        /// Teleport this transform forward by 1 meter
        /// </summary>
        public void Teleport() {
            TransformMovements.Teleport(this.transform, this.transform.position + this.transform.forward);
        }
    }

    public static class TransformMovements {
        /// <summary>
        /// Move the transform forward
        /// </summary>
        /// <param name="transform">The transform to move</param>
        /// <param name="z">The distance to move. Negative is moving backwards</param>
        public static void MoveForward(this Transform transform, float z) {
            transform.Translate(Vector3.forward * z);
        }

        /// <summary>
        /// Move the transform sidewards
        /// </summary>
        /// <param name="transform">The transform to move</param>
        /// <param name="x">The distance to move. Positive is move to the right</param>
        public static void MoveSidewards(this Transform transform, float x) {
            transform.Translate(Vector3.right * x);
        }

        /// <summary>
        /// Rotate the transform along the Y axis
        /// </summary>
        /// <param name="transform">The transform to turn</param>
        /// <param name="angle">The angle in degrees. Positive is rotate to the right</param>
        public static void Turn(this Transform transform, float angle) {
            if (angle != 0)
                transform.Rotate(0, angle, 0);
        }

        /// <summary>
        /// Teleport the transform to the target position
        /// </summary>
        /// <param name="transform">The transform to teleport</param>
        /// <param name="targetPosition">The target position to teleport to</param>
        public static void Teleport(this Transform transform, Vector3 targetPosition) {
            //Rigidbody transformRigidbody = transform.GetComponent<Rigidbody>();
            //if (transformRigidbody != null)
            //    transformRigidbody.MovePosition(targetPosition);
            //else
                transform.position = targetPosition;
        }

        private const float minSpeedMps = 50.0f; // clamped to minimum speed 50m/s to avoid sickness
        private const float normalLerpTime = 0.1f; // 100ms for every dash above minDistanceForNormalLerp
        private const float minDistanceForNormalLerp = minSpeedMps * normalLerpTime; // default values give 5.0f;
        private static float lerpTime = 5; //0.1f;

        /// <summary>
        /// Dashes the transform to the target position
        /// This needs to be called using MonoBehaviour.StartCoroutine
        /// </summary>
        /// <param name="transform">The transform to move</param>
        /// <param name="targetPosition">The target position of the dash movement</param>
        /// <returns>Coroutine result</returns>
        public static IEnumerator DashCoroutine(Transform transform, Vector3 targetPosition) {
            float maxDistance = Vector3.Distance(transform.position, targetPosition - transform.forward * 0.5f);

            if (maxDistance >= minDistanceForNormalLerp)
                lerpTime = normalLerpTime; // fixed time for all bigger dashes
            else
                lerpTime = maxDistance / minSpeedMps; // clamped to speed for small dashes

            Vector3 startPosition = transform.position;
            float elapsedTime = 0;
            float t = 0;

            while (t < 1) {
                transform.position = Vector3.Lerp(startPosition, targetPosition, t);
                elapsedTime += Time.deltaTime;
                t = elapsedTime / lerpTime;
                yield return new WaitForEndOfFrame();
            }
            transform.position = targetPosition;
        }
    }
}
