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

/// <summary>
/// Class for dealing with generic "Dumb" enemy movement AI
/// </summary>
public class EnemyHoverMovement : 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 jumpForce;
    public float jumpMaxTime;

    private float _residualThrust;
    private float _residualThrustCurrentTime;

    private float _currentJumpTime;
    private bool _isJumping;

    // Variables for RNG movement
    private float _rngVert;
    private float _rngHorz;
    private bool _rngJump;

    public float maxRNGThrust;
    public float movementDecayRate;
    public float movementTickMax;    
    private float _movementCurrentTick;

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

        _rngJump = false;
        _rngHorz = 0.0f;
        _rngVert = 0.0f;

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

    /// <summary>
    /// Calculates some RNG movement for the attached Enemy.
    /// </summary>
    /// <param name="currentTimeStep">The time unit currently used, typically deltatime or deltafixed time depending on if its the fixed update or not.</param>
    void DoRNG(float currentTimeStep)
    {
        // Check if the movement tick has passed
        if (_movementCurrentTick >= movementTickMax)
        {
            // Pick a random horz value
            if (_rngHorz == 0)
            {
                _rngHorz = Random.Range(0, maxRNGThrust);
            }

            // Pick a random vert value
            if (_rngVert == 0)
            {
                _rngVert = Random.Range(0, maxRNGThrust);
            }

            // Do an RNG check to see if the enemy jumps
            int jumpNow = Random.Range(0, 10);
            if (jumpNow > 3 || _immuneMinHit)
            {
                _rngJump = true;
            }
            else
            {
                _rngJump = false;
            }

            _movementCurrentTick = 0.0f;
        }
        else
        {
            // Reset the variables to stop it turning/moving
            if (_rngHorz > 0)
            {
                _rngHorz = Mathf.Clamp(_rngHorz - movementDecayRate, 0, maxRNGThrust); ;
            }
            if (_rngVert > 0)
            {
                _rngVert = Mathf.Clamp(_rngVert - movementDecayRate, 0, maxRNGThrust);
            }
            
            _movementCurrentTick += currentTimeStep;
        }
    }

    /// <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.
    /// Anything in this method can be removed, as long as the _turnDirection and _thrustDirection are set then the parent script with handle the orbit and positioning.
    /// </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)
    {
        // Check if the movement has been frozen
        if (!freezeMovement)
        {
            // Get the Horz and Vert axis from the RNG (or whatever vars you want)
            float horzInput = _rngHorz;
            float vertInput = _rngVert;

            // 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)
            {
                _thrusting = true;
                _thrustDirection = 1;
                if (_residualThrust < residualThrustMax)
                {
                    _residualThrust += residualThrustStep * currentTimeStep;
                }
            }
            else if (vertInput < 0)
            {
                if (_residualThrust > 0.0f)
                {
                    if (_residualThrustCurrentTime >= residualThrustTimeTickMax)
                    {
                        _residualThrust -= residualThrustDecayRate;
                    }
                    _thrustDirection = _residualThrust / residualThrustMax;
                }
                else
                {
                    _thrusting = true;
                    _thrustDirection = -1;
                }
            }
            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;
            }

            // Check if the jump button
            if (_rngJump && !_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 (_isJumping)
        {
            _objectRigidbody.AddRelativeForce(Vector3.up * jumpForce * Time.fixedDeltaTime, ForceMode.Impulse);
            if (_currentJumpTime >= jumpMaxTime)
            {
                _currentJumpTime = 0.0f;
                _isJumping = false;
                _rngJump = false;

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

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

    // Update is called once per frame
    void Update()
    {
        if (objectIsActive)
        {
            DoRNG(Time.deltaTime);
            ProcessPreMovement(Time.deltaTime);
            ProcessCurrentMovement(Time.deltaTime);
            ProcessPostMovement();
        }
    }
}
