using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// Class for dealing with the players movement, it inherits the the hovermovement class which mostly deals with making sure everything orbits the black hole
/// </summary>
public class PlayerHoverMovement : HoverMovement
{
    // Residual Thrust variables to handle the "glide" after the gameObject has moved
    public float residualThrustStep;
    public float residualThrustMax;    
    public float residualThrustTimeTickMax;
    public float residualThrustDecayRate;
    public float residualThrustIdleDecayRate;

    public float testHeight;

    public float jumpForce;
    public float jumpMaxTime;

    public Spedometer speedUI;

    private float _residualThrust;
    private float _residualThrustCurrentTime;
    
    private float _currentJumpTime;
    private bool _isJumping;

    // Variables needed for the players boost ability
    public bool canBoost;
    public float boostModifier;
    public float maxBoostTime;
    public float boostCooldown;
    private float _currentBoostTick;
    private float _currentBoostCooldownTick;
    private bool _isBoosting;
    private bool _boostingCoolingDown;

    // Bool to stop the player from reveresing direction
    public bool canReverse;

    // SFX audio clips for the player
    public AudioClip boostSFX;
    public AudioClip cantBoostSFX;
    private AudioSource _currentAudioSource;

    // Variables for dealing with the boost trails
    public Gradient boosterTrailColor;
    private TrailRenderer[] _playerTrails;
    private Gradient _defaultTrailGradient;
    private float _defaultTrailMultiplier;

    /// <summary>
    /// Method for toggling the boost trail particle effects for the player
    /// </summary>
    private void ToggleBoostEffects()
    {
        // Loop through each child trailrender and apply the toggled effect
        foreach (TrailRenderer currentTrail in _playerTrails)
        {
            if (_isBoosting)
            {
                currentTrail.colorGradient = boosterTrailColor;
                currentTrail.widthMultiplier = _defaultTrailMultiplier * 2f;
            }
            else
            {
                currentTrail.colorGradient = _defaultTrailGradient;
                currentTrail.widthMultiplier = _defaultTrailMultiplier;
            }
        }
    }

    // Start is called before the first frame update
    void Start()
    {
        _residualThrust = 0.0f;
        _residualThrustCurrentTime = 0.0f;
        _currentJumpTime = 0.0f;

        _currentBoostTick = 0.0f;
        _currentBoostCooldownTick = 0.0f;
        _isBoosting = false;
        _boostingCoolingDown = false;

        _isJumping = false;
        BaseOnStart();
        // Custom start stuff can go here        
    }

    /// <summary>
    /// Method for doing tasks that need doing during the AWake call, since an inherited script can only have 1 awake shared between everything
    /// </summary>
    public override void DoAwakeTasks()
    {
        // Get all the child trails
        _playerTrails = GetComponentsInChildren<TrailRenderer>();
        if (_playerTrails.Length > 0)
        {
            _defaultTrailGradient = _playerTrails[0].colorGradient;
            _defaultTrailMultiplier = _playerTrails[0].widthMultiplier;
        }       

        _currentAudioSource = gameObject.GetComponent<AudioSource>();
        base.DoAwakeTasks();
    }

    /// <summary>
    /// Processes the inputs needed to move the gameObject, the turn direction, thrust direction and any residual thrust calculations so that we dont just stop in place.
    /// </summary>
    /// <param name="currentTimeStep">The time unit currently used, typically deltatime or deltafixed time depending on if its the fixed update or not.</param>
    public override void ProcessPreMovement(float currentTimeStep)
    {
        // Get the Horz and Vert axis from the controls
        float horzInput = Input.GetAxis("Horizontal");
        float vertInput = Input.GetAxis("Vertical");        

        // Check horz direction value, pos = Right, neg = Left
        if (horzInput > 0)
        {
            _turnDirection = 1;
        }
        else if (horzInput < 0)
        {            
            _turnDirection = -1;
        }
        else
        {
            _turnDirection = 0;
        }

        // Check if the vert is pushed forwards or back, pos = forward, neg = back
        if (vertInput > 0)
        {
            // Start boosting forward and adding residual thrust
            _thrusting = true;
            // Thrust direction, pos numbers are forward, the higher the number the faster it will travel
            _thrustDirection = 1;
            // Check if we are boosting, if so apply the boost modifier
            if (_isBoosting)
            {
                _thrustDirection *= boostModifier;
            }
            
            // Check if we still have residual thrust to add
            if (_residualThrust < residualThrustMax)
            {
                _residualThrust += residualThrustStep * currentTimeStep;
            }
        }
        // If the player is holding backwards
        else if (vertInput < 0)
        {
            // If they have residual thrust we drain this faster when back is held
            if (_residualThrust > 0.0f)
            {
                if (_residualThrustCurrentTime >= residualThrustTimeTickMax)
                {
                    _residualThrust -= residualThrustDecayRate;                    
                }                
                _thrustDirection = _residualThrust / residualThrustMax;                
            }
            else
            {
                // If we have no residual thrust and the player can reverse, reverse
                if (canReverse)
                {
                    _thrusting = true;
                    _thrustDirection = -1;
                }
            }
        }
        // If no direction is held go through the residual thrust until depleted
        else
        {
            if (_residualThrust > 0.0f)
            {
                if (_residualThrustCurrentTime >= residualThrustTimeTickMax)
                {
                    _residualThrust -= residualThrustIdleDecayRate;                    
                }
                _thrustDirection = _residualThrust / residualThrustMax;
            }
            else
            {
                _thrusting = false;
                _thrustDirection = 0;
            }
        }

        // Clamp the residual thrust so we cant fall below 0 or greater than the max value set
        _residualThrust = Mathf.Clamp(_residualThrust, 0, residualThrustMax);
        if (_residualThrustCurrentTime >= residualThrustTimeTickMax)
        {
            _residualThrustCurrentTime = 0.0f;
        }
        else
        {            
            _residualThrustCurrentTime += currentTimeStep;
        }

        // If the player has boost enabled check for the fire button
        if (canBoost)
        {
            if (Input.GetButtonDown("Fire1") && !_isBoosting)
            {
                // Check if the boost is on cooldown otherwise boost
                if (!_boostingCoolingDown)
                {                    
                    _isBoosting = true;
                    ToggleBoostEffects();
                    if (_currentAudioSource)
                    {
                        _currentAudioSource.PlayOneShot(boostSFX);
                    }
                }                
                else
                {
                    if (_currentAudioSource)
                    {
                        _currentAudioSource.PlayOneShot(cantBoostSFX);
                    }
                }
            }
            else
            {
                // If boosting check if we have boosted for long enough
                if (_isBoosting)
                {
                    if (_currentBoostTick >= maxBoostTime)
                    {
                        _isBoosting = false;
                        _currentBoostTick = 0.0f;
                        _boostingCoolingDown = true;
                        ToggleBoostEffects();
                    }
                    else
                    {
                        _currentBoostTick += currentTimeStep;
                    }
                }
            }

            // Reduce the boost cooldown timer when on cooldown
            if (_boostingCoolingDown)
            {
                if (_currentBoostCooldownTick >= boostCooldown)
                {
                    _currentBoostCooldownTick = 0.0f;
                    _boostingCoolingDown = false;
                }
                else
                {
                    _currentBoostCooldownTick += currentTimeStep;
                }
            }
        }
        else
        {
            _isBoosting = false;
            if (_currentAudioSource)
            {
                _currentAudioSource.PlayOneShot(cantBoostSFX);
            }
        }

        // Check if the jump button
        if (Input.GetButtonDown("Jump") && !_isJumping)
        {
            _isJumping = true;            
        }

        // Run any base class stuff
        base.ProcessPreMovement(currentTimeStep);        
    }

    /// <summary>
    /// Method for dealing with the jump physics, this should be in the fixed update since it uses rigidbody physics
    /// </summary>
    void ProcessJump()
    {
        // If the player is jumping add the jump force
        if (_isJumping)
        {
            _objectRigidbody.AddRelativeForce(Vector3.up * jumpForce * Time.fixedDeltaTime, ForceMode.Impulse);
            if (_currentJumpTime >= jumpMaxTime)
            {
                _currentJumpTime = 0.0f;
                _isJumping = false;

            }
            else
            {
                _currentJumpTime += Time.fixedDeltaTime;
            }            
        }
    }

    private void FixedUpdate()
    {
        if (objectIsActive)
        {
            ProcessJump();
        }
    }

    // Update is called once per frame
    void Update()
    {
        testHeight = _currentHeight;
        if (objectIsActive)
        {
            ProcessPreMovement(Time.deltaTime);
            ProcessCurrentMovement(Time.deltaTime);
            ProcessPostMovement();
            
            speedUI.SpeedSlider(_thrustDirection);
        }
    }
}
