// FlexiMotion // https://kybernetik.com.au/flexi-motion // Copyright 2023-2025 Kybernetik //

using FlexiMotion.Jobs;
using System.Runtime.CompilerServices;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;

namespace FlexiMotion.Modifiers
{
    /// <summary>A <see cref="FlexiMotionModifier"/> which accelerates the simulated objects.</summary>
    /// https://kybernetik.com.au/flexi-motion/api/FlexiMotion.Modifiers/AccelerationModifier
    /// 
    [AddComponentMenu(FMStrings.ModifiersMenuPrefix + "Acceleration Modifier")]
    [HelpURL(FMStrings.DocsURLs.ModifiersAPIDocumentation + "/" + nameof(AccelerationModifier))]
    public class AccelerationModifier : FlexiMotionModifier,
        JobModifierGroup.IOnInitialize,
        JobModifierGroup.IOnUpdate
    {
        /************************************************************************************************************************/

        /// <summary>Execute fairly early.</summary>
        public const int ExecutionTime = 100;

        /// <summary>A multiplier for the amount of velocity applied.</summary>
        /// <remarks>
        /// This allows the <see cref="Acceleration"/> to be in a more natural range rather than needing to use very
        /// small values to get reasonable results.
        /// </remarks>
        public const float
            Magnitude = 0.01f;

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

        [SerializeField, Tooltip("The amount of acceleration to apply every frame.")]
        private Vector3 _Acceleration;

        /// <summary>[<see cref="SerializeField"/>] The amount of acceleration to apply every frame.</summary>
        public ref Vector3 Acceleration => ref _Acceleration;

        private Float3OffsetJob _Job;

        /// <summary>The job used to execute the acceleration.</summary>
        public ref Float3OffsetJob Job => ref _Job;

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

        /// <inheritdoc/>
        protected override void Enable(JobModifierGroup modifiers)
        {
            base.Enable(modifiers);

            modifiers.AddOnInitialize(ExecutionTime, this);
            modifiers.OnUpdate.Add(ExecutionTime, this);
        }

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

        /// <inheritdoc/>
        protected override bool Disable(JobModifierGroup modifiers)
        {
            base.Disable(modifiers);

            var wasEnabled = false;
            wasEnabled |= modifiers.OnInitialize.Remove(ExecutionTime);
            wasEnabled |= modifiers.OnUpdate.Remove(ExecutionTime);
            return wasEnabled;
        }

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

        /// <inheritdoc/>
        public virtual JobHandle OnInitialize(JobHandle dependsOn = default)
        {
            _Job.values = Target.Runtime.RuntimeSprings.TailPositions;
            return dependsOn;
        }

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

        /// <inheritdoc/>
        public virtual JobHandle OnUpdate(JobHandle dependsOn = default)
        {
            return ScheduleVelocity(_Acceleration * Magnitude, dependsOn);
        }

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

        /// <summary>Schedules the <see cref="Job"/> to move its positions.</summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public JobHandle SchedulePosition(float3 movement, JobHandle dependsOn = default)
        {
            _Job.offset = movement;
            return _Job.Schedule(dependsOn);
        }

        /// <summary>Schedules the <see cref="Job"/> to add to its velocities.</summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public JobHandle ScheduleVelocity(float3 velocity, JobHandle dependsOn = default)
        {
            _Job.offset = velocity * DeltaTime;
            return _Job.Schedule(dependsOn);
        }

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

