using UnityEngine;
using Cinemachine;
using System.Collections;
using ftt.Core.Managing;

/* CLASS DOCUMENTATION *\
* [Variable Specifics]
* Inspector values: Inspector values must be set from the editor inpsector for the script to work correctly
* Dynamically changed: These variables are changed throughout the game.
* 
* [Class Flow]
* 1. This script is responsible for the player movement and player camera controls.
*/

public class PlayerController : MonoBehaviour
{
    [Header("Set in inspector")]

    [Header("Move Stats")]
    [SerializeField] CinemachineVirtualCamera playerCamera;
    [SerializeField] float normalSpeed = 3f;

    [SerializeField] float maxSpeed = 8f;

    [Header("Sound stuff")]
    [SerializeField] AudioClip[] stepsClips;
    [SerializeField] bool isMoving = false;

    //Min and Max camera views
    [SerializeField] float minCameraView = -70f;
    [SerializeField] float maxCameraView = 80f;

    [SerializeField] float maxStamina = 5f;

    [SerializeField] float staminaDrain = 1f;

    [Header("Sprint settings")]
    [SerializeField] float stamina;

    #region PRIVATE_VARIABLES
    AudioSource audioSource;
    float currentInterval;
    float stepInterval = 1f;

    //Keyboard Input
    float inputVer;
    float inputHor;
    float currentSpeed;

    //Mouse Input
    float mouseInputX;
    float mouseInputY;
    float mouseXRotate;

    float gravity = 9.18f;

    bool canSprint = false;
    bool hasRenegerated = true;

    Vector3 movement;

    CharacterController charCtrl;
    Animator animator;
    #endregion

    private void Awake()
    {
        GameManager.GetManager.SetPlayerEntity(this);
    }

    private void Start()
    {
        currentSpeed = normalSpeed;
        stamina = maxStamina;

        CacheComponents();
    }

    /// <summary>
    /// Call to cache the required gameObject components.
    /// </summary>

    void CacheComponents()
    {
        audioSource = GetComponent<AudioSource>();
        charCtrl = GetComponent<CharacterController>();
        animator = GetComponent<Animator>();
    }

    void Update()
    {
        if (GameManager.GetManager.CurrentState == GameStates.ENTRY ||
            GameManager.GetManager.IsPlayerHidden) { return; }

        Movement();

        Rotation();

        StaminaRegen();

        if (movement.x == 0 && movement.z == 0)
        {
            isMoving = false;
        }
        else
        {
            isMoving = true;
            if (isMoving)
            {
                currentInterval -= Time.deltaTime;
                if (currentInterval <= 0)
                {
                    AudioClip audioIDX = stepsClips[Random.Range(0, stepsClips.Length)];
                    audioSource.PlayOneShot(audioIDX);
                    currentInterval = stepInterval;
                }
            }
            isMoving = false;
        }
    }

    private void Movement()
    {
        inputHor = Input.GetAxis("Horizontal");
        inputVer = Input.GetAxis("Vertical");

        movement = transform.forward * inputVer + transform.right * inputHor;

        movement.y -= gravity;

        if (Input.GetKey(KeyCode.LeftShift) && Input.GetKey(KeyCode.W) && !Input.GetKey(KeyCode.S))
        {
            if (canSprint == true && charCtrl.velocity.magnitude > 0)
            {
                if (stamina > 0)
                {
                    Sprinting();
                }
                else
                {
                    stamina = 0;
                    hasRenegerated = false;
                    currentSpeed = normalSpeed;
                    canSprint = false;
                    stepInterval = 1;

                }

            }
        }

        if (Input.GetKeyUp(KeyCode.LeftShift))
        {
            canSprint = false;
            stepInterval = 1;
        }

        //Moving the character
        charCtrl.Move(movement * currentSpeed * Time.deltaTime);
    }

    private void Rotation()
    {
        mouseInputX = Input.GetAxis("Mouse X") * GameManager.GetManager.Sensitivity * Time.deltaTime;
        mouseInputY = Input.GetAxis("Mouse Y") * GameManager.GetManager.Sensitivity * Time.deltaTime;

        mouseXRotate -= mouseInputY;

        mouseXRotate = Mathf.Clamp(mouseXRotate, minCameraView, maxCameraView);

        playerCamera.transform.localRotation = Quaternion.Euler(mouseXRotate, 0, 0);

        transform.Rotate(Vector3.up * mouseInputX * 3);
    }

    private void Sprinting()
    {
        if (hasRenegerated)
        {
            stamina -= staminaDrain * Time.deltaTime;

            currentSpeed = maxSpeed;

            stepInterval = 0.3f;
        }
    }

    private void StaminaRegen()
    {
        if (!canSprint)
        {
            if (stamina <= maxStamina)
            {
                stamina += staminaDrain * Time.deltaTime;

                currentSpeed = normalSpeed;

                if (stamina >= maxStamina)
                {
                    hasRenegerated = true;
                    canSprint = true;
                    stamina = maxStamina;
                }
            }
        }
    }

    public void DeactivateController()
    {
        charCtrl.enabled = false;
    }

    public void ActivateController()
    {
        charCtrl.enabled = true;
    }

    #region BEZIER_VARS
    float uCurrent = 0;
    float uMax = 1f;

    Vector3 p012 = Vector3.zero;
    bool moving;
    float startTime;

    Vector3 startPos, midPos;
    float lerpTime = 0.5f;
    #endregion

    public void LerpPlayerToPosition(Vector3 pos)
    {
        StartCoroutine(LerpToPos(pos));
    }

    IEnumerator LerpToPos(Vector3 pos)
    {
        startPos = transform.position;
        midPos = (transform.position + pos) / 2;
        bool moving = true;
        float startTime = Time.time;

        while (moving)
        {
            Vector3 targetPos = pos;

            uCurrent = (Time.time - startTime) / lerpTime;

            if (uCurrent >= uMax)
            {
                uCurrent = uMax;
                moving = false;
            }

            Vector3 p01, p12;
            p01 = (1 - uCurrent) * startPos + uCurrent * midPos;
            p12 = (1 - uCurrent) * midPos + uCurrent * targetPos;

            p012 = (1 - uCurrent) * p01 + uCurrent * p12;

            transform.position = p012;

            yield return null;
        }

        uCurrent = 0;
        p012 = startPos = midPos = Vector3.zero;
    }
}
