using UnityEngine;

public class CowardEnemyHoverMovement : HoverMovement
{
    public GameObject minePrefab;
    public float mineSpawnChance;
    public float mineSpawnCooldown;
    private float _mineSpawnTick;
    private bool _canSpawnMine;

    // Game object to track the player GO
    private GameObject _player;
    private GameObject _blackHole;

    // 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 _aiVert;
    private float _aiHorz;
    private bool _aiJump;
        
    public float cowerDistance;
    public float emergencyBoostSpeed;
    public float emergencyBoostCooldownSpeed;
    public float emergencyBoostTime;
    public float emergencyBoostCooldown;
    private bool _emergencyBoosting;
    private bool _emergencyBoosted;
    private float _currentEmergencyBoostingTick;
    private float _currentEmergencyBoosterCooldownTick;

    private float _currentHoldHeight;
    private float _heightChangeTick;
    public float heightChangeInterval;

    public override void DoAwakeTasks()
    {
        base.DoAwakeTasks();
    }

    // Start is called before the first frame update
    void Start()
    {
        // We have to find the player since setting it in the editor doesnt work correctly        
        _player = GameObject.Find("Player");
        _blackHole = GameObject.Find("BlackHole");

        _residualThrust = 0.0f;
        _residualThrustCurrentTime = 0.0f;
        _currentJumpTime = 0.0f;
        _isJumping = false;

        _aiJump = false;
        _aiHorz = 0.0f;
        _aiVert = 0.0f;

        _emergencyBoosting = false;
        _emergencyBoosted = false;
        _currentEmergencyBoosterCooldownTick = 0.0f;
        _currentHoldHeight = GetCurrentHeight();
        _heightChangeTick = 0.0f;

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

    void DoAIPhysics(float currentTimeStep)
    {

    }

    /// <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 DoAI(float currentTimeStep)
    {
        // Get the Up vector of each gameObject so we can compare its X values rather than the Y
        Vector3 playerDirection = _player.transform.position - transform.position;
        
        Vector3 playerLocation = Vector3.ProjectOnPlane(_player.transform.position, Vector3.up);
        Vector3 myLocation = Vector3.ProjectOnPlane(transform.position, Vector3.forward);
        Vector3 direction = playerLocation - myLocation;        

        float facingPlayer = Vector3.Dot(playerDirection.normalized, transform.forward);
        
        // Added a check to see if the difference is big enough to warrant a turn otherwise they just get stuck turning forever
        float playerHeight = _player.GetComponent<PlayerHoverMovement>().GetCurrentHeight();

        _aiVert = 1;
        _aiHorz = 0.0f;

        if (playerDirection.magnitude <= cowerDistance)
        {
            if (!_emergencyBoosting && !_emergencyBoosted)
            {
                // Try to boost away                
                _emergencyBoosting = true;
                _canSpawnMine = false;
                _mineSpawnTick = 0.0f;
                // Force a height change for fun
                _heightChangeTick = heightChangeInterval;
            }
        }

        if (!(facingPlayer < 0.5f && facingPlayer > -0.5f))
        {
            _aiHorz = -direction.normalized.x;
        }

        if (_currentHoldHeight >= GetCurrentHeight())
        {
            RaycastHit aboveCheck;
            Physics.SphereCast(transform.position, 3.0f, transform.up, out aboveCheck, 5.0f, LayerMask.GetMask("Player"));
            if (aboveCheck.rigidbody != null)
            {
                if ((GetCurrentHeight() < playerHeight))
                {                    
                    _objectRigidbody.velocity = Vector3.zero;
                    _aiJump = false;
                }
                else
                {
                    _aiJump = true;
                }
            }
            else
            {
                _aiJump = true;
            }
        }
        else
        {
            _aiJump = false;
        }


        if (minePrefab != null)
        {
            if (_canSpawnMine && _emergencyBoosting)
            {                
               DoAMineSpawn();                
            }
        }
    }

    private void DoAMineSpawn()
    {
        GameObject newMine = Instantiate(minePrefab);        
        MineHoverMovement mineMovement = newMine.GetComponent<MineHoverMovement>();
        if (mineMovement)
        {
            mineMovement.startPositionVector = transform.position + (-transform.forward * 2.0f);
            mineMovement.startHeight = GetCurrentHeight() - 0.5f;
        }

        newMine.SetActive(true);
        _canSpawnMine = false;
    }

    /// <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 movement has been frozen
        if (!freezeMovement)
        {
            // Get the Horz and Vert axis from the RNG (or whatever vars you want)
            float horzInput = _aiHorz;
            float vertInput = _aiVert;

            // 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 (_emergencyBoosting)
                {
                    _thrustDirection *= emergencyBoostSpeed;
                }
                else if (_emergencyBoosted)
                {
                    _thrustDirection *= emergencyBoostCooldownSpeed;
                }
                if (_residualThrust < residualThrustMax)
                {
                    _residualThrust += residualThrustStep * currentTimeStep;
                }
            }
            // If the Enemy is holding backwards
            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 (_aiJump && !_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 enemy 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;
                _aiJump = false;

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

    void ProcessCooldowns(float currentTimeStep)
    {
        if (_emergencyBoosting)
        {
            if (_currentEmergencyBoostingTick >= emergencyBoostTime)
            {
                _emergencyBoosted = true;
                _emergencyBoosting = false;
                _currentEmergencyBoostingTick = 0.0f;
            }
            else
            {
                _currentEmergencyBoostingTick += currentTimeStep;
            }
        }

        if (_emergencyBoosted)
        {
            if (_currentEmergencyBoosterCooldownTick >= emergencyBoostCooldown)
            {
                _emergencyBoosted = false;
                _currentEmergencyBoosterCooldownTick = 0.0f;
            }
            else
            {
                _currentEmergencyBoosterCooldownTick += currentTimeStep;
            }
        }

        if (_heightChangeTick >= heightChangeInterval)
        {
            _currentHoldHeight = Random.Range(startHeight, startHeight + 5.0f);
            _heightChangeTick = 0.0f;
        }
        else
        {
            _heightChangeTick += currentTimeStep;
        }

        if (!_canSpawnMine)
        {
            if (_mineSpawnTick >= mineSpawnCooldown)
            {
                _mineSpawnTick = 0.0f;
                _canSpawnMine = true;
            }
            else
            {
                _mineSpawnTick += currentTimeStep;
            }
        }
    }

    private void FixedUpdate()
    {
        if (objectIsActive)
        {
            DoAIPhysics(Time.fixedDeltaTime);
            ProcessJump();
        }
    }

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