// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //

using UnityEngine;

namespace Animancer
{
    /// <summary>A <see cref="MixerParameterTween{TParameter}"/> which uses <see cref="Mathf.LerpUnclamped"/>.</summary>
    /// 
    /// <remarks>
    /// <para></para>
    /// <strong>Documentation:</strong>
    /// <see href="https://kybernetik.com.au/animancer/docs/manual/blending/mixers#smoothing">
    /// Smoothing</see>
    /// <para></para>
    /// <strong>Example:</strong><code>
    /// [SerializeField] private AnimancerComponent _Animancer;
    /// [SerializeField] private LinearMixerTransition _Mixer;
    /// 
    /// private MixerParameterTweenFloat _MixerTween;
    /// 
    /// private void Awake()
    /// {
    ///     // Play creates the LinearMixerState from the transition.
    ///     _Animancer.Play(_Mixer);
    /// 
    ///     // Now that the state exists, we can create a tween for it.
    ///     _MixerTween = new MixerParameterTweenFloat(_Mixer.State);
    /// 
    ///     // Start tweening the parameter towards 0.5 over a period of 0.25 seconds.
    ///     _MixerTween.Start(0.5f, 0.25f);
    /// }
    /// </code></remarks>
    /// 
    /// https://kybernetik.com.au/animancer/api/Animancer/MixerParameterTweenFloat
    /// 
#if !UNITY_EDITOR
    [System.Obsolete(Validate.ProOnlyMessage)]
#endif
    public class MixerParameterTweenFloat : MixerParameterTween<float>
    {
        public MixerParameterTweenFloat() { }
        public MixerParameterTweenFloat(MixerState<float> mixer) : base(mixer) { }

        protected override float CalculateCurrentValue() => Mathf.LerpUnclamped(StartValue, EndValue, Progress);
    }

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

    /// <summary>A <see cref="MixerParameterTween{TParameter}"/> which uses <see cref="Vector2.LerpUnclamped"/>.</summary>
    /// <remarks>See <see cref="MixerParameterTweenFloat"/> for a usage example.</remarks>
    /// https://kybernetik.com.au/animancer/api/Animancer/MixerParameterTweenVector2
    /// 
#if !UNITY_EDITOR
    [System.Obsolete(Validate.ProOnlyMessage)]
#endif
    public class MixerParameterTweenVector2 : MixerParameterTween<Vector2>
    {
        public MixerParameterTweenVector2() { }
        public MixerParameterTweenVector2(MixerState<Vector2> mixer) : base(mixer) { }

        protected override Vector2 CalculateCurrentValue() => Vector2.LerpUnclamped(StartValue, EndValue, Progress);
    }

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

    /// <summary>A system which interpolates a <see cref="MixerState{TParameter}.Parameter"/> over time.</summary>
    /// <remarks>See <see cref="MixerParameterTweenFloat"/> for a usage example.</remarks>
    /// https://kybernetik.com.au/animancer/api/Animancer/MixerParameterTween_1
    /// 
#if !UNITY_EDITOR
    [System.Obsolete(Validate.ProOnlyMessage)]
#endif
    public abstract class MixerParameterTween<TParameter> : Updatable
    {
        /************************************************************************************************************************/

        /// <summary>The target <see cref="MixerState{TParameter}"/>.</summary>
        public MixerState<TParameter> Mixer { get; set; }

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

        /// <summary>The value of the <see cref="MixerState{TParameter}.Parameter"/> when this tween started.</summary>
        public TParameter StartValue { get; set; }

        /// <summary>The target value this tween is moving the <see cref="MixerState{TParameter}.Parameter"/> towards.</summary>
        public TParameter EndValue { get; set; }

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

        /// <summary>The amount of time this tween will take (in seconds).</summary>
        public float Duration { get; set; }

        /// <summary>The amount of time that has passed since the <see cref="Start"/> (in seconds).</summary>
        public float Time { get; set; }

        /// <summary>The normalized progress (0 to 1) of this tween towards its goal.</summary>
        public float Progress
        {
            get => Time / Duration;
            set => Time = value * Duration;
        }

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

        /// <summary>Creates a new <see cref="MixerParameterTween{TParameter}"/>.</summary>
        public MixerParameterTween() { }

        /// <summary>Creates a new <see cref="MixerParameterTween{TParameter}"/> and sets the <see cref="Mixer"/>.</summary>
        public MixerParameterTween(MixerState<TParameter> mixer) => Mixer = mixer;

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

        /// <summary>
        /// Sets the details of this tween and registers it to be updated so that it can apply its effects every frame.
        /// </summary>
        public void Start(TParameter endValue, float duration)
        {
#if UNITY_ASSERTIONS
            AnimancerUtilities.Assert(Mixer != null, nameof(Mixer) + " is null.");
            AnimancerUtilities.Assert(Mixer.Graph != null, $"{nameof(Mixer)}.{nameof(Mixer.Graph)} is null.");
#endif

            StartValue = Mixer.Parameter;
            EndValue = endValue;
            Duration = duration;
            Time = 0;

            Mixer.Graph.RequirePreUpdate(this);
        }

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

        /// <summary>Stops this tween from updating.</summary>
        public void Stop() => Mixer?.Graph?.CancelPreUpdate(this);

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

        /// <summary>Is this tween currently being updated?</summary>
        public bool IsActive => this.IsInList();

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

        /// <summary>
        /// Called every update while this tween is active to calculate the what value to set the
        /// <see cref="MixerState{TParameter}.Parameter"/> to. Usually based on the <see cref="StartValue"/>,
        /// <see cref="EndValue"/>, and <see cref="Progress"/>.
        /// </summary>
        protected abstract TParameter CalculateCurrentValue();

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

        public override void Update()
        {
            Time += AnimancerGraph.DeltaTime;

            if (Time < Duration)// Tween.
            {
                Mixer.Parameter = CalculateCurrentValue();
            }
            else// End.
            {
                Time = Duration;
                Mixer.Parameter = EndValue;
                Stop();
            }
        }

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

