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

/// <summary>
/// Class for dealing with all of the movement calculations around the blackhole, this should be inherited by another script and extended to meet its purpose
/// </summary>
public class HoverMovement : MonoBehaviour
{
    // Freezes all movement for the attached gameobject
    public bool freezeMovement;

    public bool objectIsActive = true;

    public float testBaseHeight;

    public GameObject objectModel;

    public float startHeight;
    public Vector3 startPositionVector = Vector3.zero;

    public float turnSpeed;
    public float smoothRollTime;
    public float smoothThrusting;
    public float rollAngle;    
    public float turnSmoothTime;
    public float maxThrust;

    private float _workingTurnSpeed;
    private float _workingRollAngle;
    private float _workingMaxThrust;

    protected int _turnDirection;
    protected float _smoothedTurnSpeed;
    protected float _smoothTurnV;
    protected float _smoothRollV;
    protected float _currentThrust;

    protected float _currentHeight;    
    protected float _currentRollAngle;

    protected bool _thrusting;
    protected float _thrustDirection;

    public bool blackHoleImmune;
    public float immuneMinHeight;
    protected bool _immuneMinHit;

    protected bool _noMovement;

    protected GameEngine _currentGameEngine;
    protected Rigidbody _objectRigidbody;
    protected ObjectShared _currentObjectShared;

    private void Awake()
    {
        _currentGameEngine = FindObjectOfType<GameEngine>();
        _objectRigidbody = GetComponent<Rigidbody>();
        _currentObjectShared = GetComponent<ObjectShared>();
        _noMovement = false;       
        DoAwakeTasks();
    }

    /// <summary>
    /// Method for allowing a child script to execute code during the awake call
    /// </summary>
    protected virtual void DoAwakeTasks()
    {
    }

    /// <summary>
    /// Method for forcing a gameObject to a specific height given
    /// </summary>
    /// <param name="newHeight">Height distance from the blackhole</param>
    public void ForceHeightAdjust(float newHeight)
    {
        startHeight = newHeight;
        _currentHeight = newHeight;
    }

    /// <summary>
    /// Method to force the current GO back to its start location
    /// </summary>
    public void ForceStartLocationReset()
    {
        float blackHoleSize = _currentGameEngine.GetCurrentBlackHoleRadius();
        _currentHeight = startHeight; // 1 + (transform.position.y - blackHoleInitialSize) / blackHoleInitialSize;            
        _thrusting = false;

        Vector3 startPos = startPositionVector + transform.forward;
        Vector3 testPos = startPos.normalized * (blackHoleSize * _currentHeight);
        float heightDistance = (testPos - _currentGameEngine.currentBlackHole.transform.position).magnitude;
            
        if (heightDistance >= _currentGameEngine.arenaSize)
        {
            _currentHeight = (_currentGameEngine.arenaSize - 5.0f) / blackHoleSize;            
        }        
        startPos = startPos.normalized * (blackHoleSize * _currentHeight);
        transform.position = startPos;
    }

    /// <summary>
    /// Method for run start call jobs once the child has
    /// </summary>
    protected void BaseOnStart()
    {
        if (!_currentGameEngine)
        {
            objectIsActive = false;            
        }
        else
        {
            ForceStartLocationReset();
        }
    }

    /// <summary>
    /// Method to get the current height of this object
    /// </summary>
    /// <returns>Current height as a float</returns>
    public float GetCurrentHeight()
    {
        return _currentHeight;
    }

    protected virtual void ProcessWorkingStartValues(float currentTimeStep)
    {
        _workingTurnSpeed = turnSpeed;
        _workingRollAngle = rollAngle;
        _workingMaxThrust = maxThrust;
    }

    /// <summary>
    /// Method for the child scripts to setup the variables needed for movement
    /// </summary>
    /// <param name="currentTimeStep">Time scale used, depends if this is in fixedUpdate or update.</param>
    protected virtual void ProcessPreMovement(float currentTimeStep)
    {        
    }

    /// <summary>
    /// Method that processes the actual movement around the blackhole using some wizard math
    /// </summary>
    /// <param name="currentTimeStep">Time step currently used</param>
    protected virtual void ProcessCurrentMovement(float currentTimeStep)
    {
        float heightDistance = (transform.position - _currentGameEngine.currentBlackHole.transform.position).magnitude;
        if (heightDistance >= _currentGameEngine.arenaSize)
        {
            heightDistance = _currentGameEngine.arenaSize;
        }
        float currentBlackHoleSize = _currentGameEngine.GetCurrentBlackHoleRadius();        
        _currentHeight = heightDistance / currentBlackHoleSize;

        testBaseHeight = heightDistance;

        if (blackHoleImmune)
        {
            float relImmuneHeight = immuneMinHeight / currentBlackHoleSize;
            if (_currentHeight < relImmuneHeight)
            {
                _currentHeight = relImmuneHeight;
                _immuneMinHit = true;
            }            
            else
            {
                _immuneMinHit = false;
            }
        }

        _smoothedTurnSpeed = Mathf.SmoothDamp(_smoothedTurnSpeed, _turnDirection * _workingTurnSpeed, ref _smoothTurnV, turnSmoothTime);
        float turnAmount = _smoothedTurnSpeed * currentTimeStep;

        transform.RotateAround(transform.position, transform.up, turnAmount);

        _currentThrust = Mathf.Lerp(_currentThrust, _workingMaxThrust, 1 - Mathf.Pow(smoothThrusting, currentTimeStep));
        float thrustSpeed = (Mathf.Cos(1 * Mathf.Deg2Rad) * _currentThrust) * _thrustDirection;

        if (!_noMovement)
        {
            Vector3 movePos = transform.position + transform.forward * thrustSpeed * currentTimeStep;
            movePos = movePos.normalized * (_currentGameEngine.GetCurrentBlackHoleRadius() * _currentHeight);
            transform.position = movePos;
        }

        ProcessRotation();

        float targetRoll = _turnDirection * _workingRollAngle;
        _currentRollAngle = Mathf.SmoothDampAngle(_currentRollAngle, targetRoll, ref _smoothRollV, smoothRollTime);
    }

    /// <summary>
    /// Method to check if the gameobject has hit the min height and become gravity immune
    /// </summary>
    /// <returns>True if the min height hit</returns>
    public bool IsGravityImmune()
    {
        return _immuneMinHit;
    }

    /// <summary>
    /// Method for processing the rotation of the model
    /// </summary>
    protected virtual void ProcessRotation()
    {
        Vector3 gravityUp = transform.position.normalized;
        transform.rotation = Quaternion.FromToRotation(transform.up, gravityUp) * transform.rotation;
        transform.LookAt((transform.position + transform.forward * 10).normalized * (_currentGameEngine.GetCurrentBlackHoleRadius() * _currentHeight), gravityUp);
    }

    protected virtual void ProcessPostMovement()
    {
        if (objectModel != null)
        {
            objectModel.transform.localEulerAngles = new Vector3(1, 0, _currentRollAngle);
        }
    }
}
