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

/// <summary>
/// Class for dealing with a basic follow enemy AI
/// </summary>
public class BasicFollowEnemyHoverMovement : HoverMovement
{
    // 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 chargeRadius;
    public float chargePower;
    public float chargeCooldown;
    private bool _charged;
    private float _currentChargeCooldownTick;
    private RaycastHit _rayHitPosition;


    protected 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;

        _charged = false;
        _currentChargeCooldownTick = 0.0f;

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

    void DoAIPhysics(float currentTimeStep)
    {
        if (_rayHitPosition.rigidbody != null)
        {
            if (_rayHitPosition.rigidbody.CompareTag("Player") && _rayHitPosition.distance <= 5.0f)
            {
                if (!_charged)
                {
                    float playerHeight = _player.GetComponent<PlayerHoverMovement>().GetCurrentHeight();

                    if (playerHeight < GetCurrentHeight())
                    {                        
                        _objectRigidbody.AddRelativeForce((Vector3.forward + Vector3.down) * chargePower);
                        _charged = true;
                    }
                }
            }
        }
    }

    /// <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 angleDiff = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
        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();
        Physics.SphereCast(transform.position, chargeRadius, transform.forward, out _rayHitPosition);
                
        _aiVert = 1;
        if (facingPlayer > 0.8f)
        {
            _aiHorz = 0.0f;

            if (_rayHitPosition.rigidbody != null)
            {
                if (_rayHitPosition.rigidbody.CompareTag("Player") && _rayHitPosition.distance <= 15.0f)
                {
                    if (!_charged)
                    {
                        if (playerHeight > GetCurrentHeight())
                        {
                            _objectRigidbody.velocity = Vector3.zero;
                            _aiJump = false;
                        }
                        else
                        {
                            _aiVert = 0.5f;
                            _aiJump = true;
                        }
                    }                    
                }
                else
                {
                    if (playerHeight > GetCurrentHeight())
                    {
                        _aiJump = true;
                    }
                }
            }
        }       
        else
        {
            _aiHorz = angleDiff;
            if (((playerHeight - 0.5f) >= GetCurrentHeight()))
            {               
                if (_rayHitPosition.rigidbody != null)
                {                    
                    if (_rayHitPosition.rigidbody.CompareTag("Player") && (GetCurrentHeight() < playerHeight))
                    {                        
                        Debug.LogError("PLAYER ABOVE DONT JUMP (plz)");
                        _objectRigidbody.velocity = Vector3.zero;
                        _aiJump = false;
                    }
                    else
                    {
                        _aiJump = true;
                    }
                }
                else
                {
                    _aiJump = true;
                }
            }
            else
            {
                _aiJump = 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>
    protected 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 (_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 (_charged)
        {
            if (_currentChargeCooldownTick >= chargeCooldown)
            {
                _charged = false;
                _currentChargeCooldownTick = 0.0f;
            }
            else
            {
                _currentChargeCooldownTick += currentTimeStep;
            }
        }
    }

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

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