// Animancer // Copyright 2020 Kybernetik //

#pragma warning disable CS0618 // Type or member is obsolete (for HybridAnimancerComponent in Animancer Lite).

using Animancer.Examples.StateMachines.Brains;
using UnityEngine;

namespace Animancer.Examples.AnimatorControllers
{
    /// <summary>
    /// A <see cref="CreatureState"/> which moves the creature according to their
    /// <see cref="CreatureBrain.MovementDirection"/>.
    /// </summary>
    /// <remarks>
    /// This class is very similar to <see cref="StateMachines.Brains.LocomotionState"/>, except that it manages a
    /// Blend Tree instead of individual <see cref="AnimationClip"/>s.
    /// </remarks>
    [AddComponentMenu(Strings.MenuPrefix + "Examples/Hybrid - Locomotion State")]
    [HelpURL(Strings.APIDocumentationURL + ".Examples.AnimatorControllers/LocomotionState")]
    public sealed class LocomotionState : CreatureState
    {
        /************************************************************************************************************************/

        [SerializeField] private float _Acceleration = 3;

        private float _MoveBlend;

        /************************************************************************************************************************/

        private void OnEnable()
        {
            Animancer.PlayController();
            _MoveBlend = 0;
        }

        /************************************************************************************************************************/

        private void Update()
        {
            UpdateAnimation();
            UpdateTurning();
        }

        /************************************************************************************************************************/

        /// <summary>
        /// This method is similar to the "PlayMove" method in <see cref="Locomotion.IdleAndWalkAndRun"/>, but instead
        /// of checking <see cref="Input"/> to determine whether or not to run we are checking if the
        /// <see cref="Creature.Brain"/> says it wants to run.
        /// </summary>
        private void UpdateAnimation()
        {
            float targetBlend;
            if (Creature.Brain.MovementDirection == Vector3.zero)
                targetBlend = 0;
            else if (Creature.Brain.IsRunning)
                targetBlend = 1;
            else
                targetBlend = 0.5f;

            _MoveBlend = Mathf.MoveTowards(_MoveBlend, targetBlend, _Acceleration * Time.deltaTime);
            Animancer.SetFloat("MoveBlend", _MoveBlend);
        }

        /************************************************************************************************************************/

        /// <remarks>
        /// This method is identical to the same method in <see cref="StateMachines.Brains.LocomotionState"/>.
        /// </remarks>
        private void UpdateTurning()
        {
            var movement = Creature.Brain.MovementDirection;
            if (movement == Vector3.zero)
                return;

            var targetAngle = Mathf.Atan2(movement.x, movement.z) * Mathf.Rad2Deg;
            var turnDelta = Creature.Stats.TurnSpeed * Time.deltaTime;

            var transform = Creature.Animancer.transform;
            var eulerAngles = transform.eulerAngles;
            eulerAngles.y = Mathf.MoveTowardsAngle(eulerAngles.y, targetAngle, turnDelta);
            transform.eulerAngles = eulerAngles;
        }

        /************************************************************************************************************************/

        /// <summary>
        /// Constantly moves the creature according to the <see cref="CreatureBrain.MovementDirection"/>.
        /// </summary>
        /// <remarks>
        /// This method is kept simple for the sake of demonstrating the animation system in this example.
        /// In a real game you would usually not set the velocity directly, but would instead use
        /// <see cref="Rigidbody.AddForce"/> to avoid interfering with collisions and other forces.
        /// <para></para>
        /// This method is identical to the same method in <see cref="StateMachines.Brains.LocomotionState"/>.
        /// </remarks>
        private void FixedUpdate()
        {
            // Get the desired direction, remove any vertical component, and limit the magnitude to 1 or less.
            // Otherwise a brain could make the creature travel at any speed by setting a longer vector.
            var direction = Creature.Brain.MovementDirection;
            direction.y = 0;
            direction = Vector3.ClampMagnitude(direction, 1);

            var speed = Creature.Stats.GetMoveSpeed(Creature.Brain.IsRunning);

            Creature.Rigidbody.velocity = direction * speed;
        }

        /************************************************************************************************************************/

        /// <summary>
        /// Normally the <see cref="Creature"/> class would have a reference to the specific type of
        /// <see cref="AnimancerComponent"/> we want, but for the sake of reusing code from the earlier example, we
        /// just use a type cast here.
        /// </summary>
        private HybridAnimancerComponent Animancer
        {
            get { return (HybridAnimancerComponent)Creature.Animancer; }
        }

        /************************************************************************************************************************/
    }
}