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

using System;
using UnityEngine;
using Object = UnityEngine.Object;

namespace Animancer.Samples.Events
{
    /// <summary>Various utility delegates which can be assigned to Animancer Events.</summary>
    /// 
    /// <remarks>
    /// <strong>Sample:</strong>
    /// <see href="https://kybernetik.com.au/animancer/docs/samples/events/utilities">
    /// Event Utilities</see>
    /// </remarks>
    /// 
    /// https://kybernetik.com.au/animancer/api/Animancer.Samples.Events/EventUtilities
    /// 
    [AnimancerHelpUrl(typeof(EventUtilities))]
    public static class EventUtilities
    {
        /************************************************************************************************************************/
        // Since the methods in this class are intended for use with Animancer Events, they are declared as delegate fields
        // rather than regular methods so that assigning them doesn't create any garbage.
        /************************************************************************************************************************/
        // If you intend to use any of these methods in your own scripts, it's recommended that you copy the ones you need into
        // your own utility class so that you can delete the Animancer Samples once you're done with them.
        /************************************************************************************************************************/

        /// <summary>
        /// Logs a message with the details of the currently triggering event.
        /// </summary>
        /// 
        /// <remarks>
        /// <strong>Sample:</strong>
        /// Go through every event in a transition and make it Log the event in addition to its normal callback:
        /// <para></para><code>
        /// [SerializeField] private ClipTransition _Transition;
        /// 
        /// protected virtual void Awake()
        /// {
        ///     for (int i = 0; i &lt; _Transition.Events.Count; i++)
        ///     {
        ///         _Transition.Events.AddCallback(i, EventUtilities.LogCurrentEvent);
        ///     }
        /// }
        /// </code></remarks>
        public static readonly Action LogCurrentEvent = () =>
        {
            AnimancerEvent.Invocation invocation = AnimancerEvent.Current;

            // Clicking on the log message in the Console will highlight this object.
            Object context = invocation.State?.Graph?.Component as Object;

            Debug.Log(
                $"An {nameof(AnimancerEvent)} was triggered:" +
                $"\n- Event: {invocation.Event}" +
                $"\n- Name: {invocation.Name}" +
                $"\n- State: {invocation.State.GetDescription()}",
                context);
        };

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

        /// <summary>
        /// Sets the <see cref="AnimancerState.Time"/> of the
        /// <see cref="AnimancerEvent.Current"/> state to 0.
        /// </summary>
        /// 
        /// <remarks>
        /// <strong>Sample:</strong>
        /// Play a non-looping animation but force it to loop:
        /// <para></para><code>
        /// [SerializeField] private AnimancerComponent _Animancer;
        /// [SerializeField] private AnimationClip _NonLoopingClip;
        /// 
        /// protected virtual void Awake()
        /// {
        ///     AnimancerState state = _Animancer.Play(_NonLoopingClip);
        ///     state.Events(this).OnEnd = EventUtilities.RestartCurrentState;
        /// }
        /// </code></remarks>
        public static readonly Action RestartCurrentState = () =>
        {
            AnimancerEvent.Current.State.Time = 0;
        };

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

        /// <summary>
        /// Pauses the <see cref="AnimancerEvent.Current"/> state at the current
        /// <see cref="AnimancerEvent.normalizedTime"/>.
        /// </summary>
        /// <remarks>
        /// This can be useful for having an animation which stops at certain times to wait for something else to
        /// happen without needing to separate the animation into separate <see cref="AnimationClip"/>s.
        /// </remarks>
        public static readonly Action PauseAtCurrentEvent = () =>
        {
            AnimancerEvent.Invocation currentEvent = AnimancerEvent.Current;
            currentEvent.State.IsPlaying = false;
            currentEvent.State.NormalizedTime = currentEvent.Event.normalizedTime;
        };

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