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

using System.Collections.Generic;
using Unity.Jobs;
using UnityEngine;

namespace FlexiMotion.Modifiers
{
    /// <summary>A <see cref="FlexiMotionModifier"/> which applies collisions against sphere colliders.</summary>
    /// https://kybernetik.com.au/flexi-motion/api/FlexiMotion.Modifiers/SphereCollidersModifier
    /// 
    [AddComponentMenu(FMStrings.ModifiersMenuPrefix + "Sphere Colliders Modifier")]
    [HelpURL(FMStrings.DocsURLs.ModifiersAPIDocumentation + "/" + nameof(SphereCollidersModifier))]
    public class SphereCollidersModifier : FlexiMotionModifier,
        JobModifierGroup.IOnInitialize,
        JobModifierGroup.IPreUpdate,
        JobModifierGroup.IOnUpdate,
        ISphereCollidersModifier,
        IOnValidate
    {
        /************************************************************************************************************************/

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

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

        /// <summary>The name of the serialized backing field of <see cref="Colliders"/>.</summary>
        public const string CollidersFieldName = nameof(_Colliders);

        [SerializeField]
        private SphereColliderDefinitions _Colliders;

        /// <summary>[<see cref="SerializeField"/>] Data from which the <see cref="Runtime"/> will be initialized.</summary>
        public ref SphereColliderDefinitions Colliders => ref _Colliders;

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

        /// <summary>The collider job manager.</summary>
        public RuntimeSphereColliders Runtime { get; private set; }

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

        /// <inheritdoc/>
        protected override void OnValidate()
        {
            base.OnValidate();

            _Colliders?.Validate();
        }

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

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

            modifiers.AddOnInitialize(ExecutionTime, this);
            modifiers.PreUpdate.Add(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.PreUpdate.Remove(ExecutionTime);
            wasEnabled |= modifiers.OnUpdate.Remove(ExecutionTime);
            return wasEnabled;
        }

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

        /// <inheritdoc/>
        public virtual JobHandle OnInitialize(JobHandle dependsOn = default)
        {
            if (Runtime == null)
            {
                Runtime = new RuntimeSphereColliders(
                    _Colliders,
                    Target.Runtime.RuntimeSprings);
            }
            else
            {
                Runtime.SphereCollidersJob.transformPositions = Target.Runtime.RuntimeSprings.TailPositions;
                Runtime.SphereCollidersJob.transformRadii = Target.Runtime.RuntimeSprings.TailRadii;
            }

            return dependsOn;
        }

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

        private JobHandle _CalculateWorldPositionsJob;

        /// <inheritdoc/>
        public virtual JobHandle PreUpdate(JobHandle dependsOn = default)
        {
            _CalculateWorldPositionsJob = Runtime.ScheduleCalculateWorldPositionsJob(dependsOn);
            return dependsOn;
        }

        /// <inheritdoc/>
        public virtual JobHandle OnUpdate(JobHandle dependsOn = default)
        {
            dependsOn = JobHandle.CombineDependencies(dependsOn, _CalculateWorldPositionsJob);
            return Runtime.ScheduleSphereCollidersJob(dependsOn);
        }

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

        /// <summary>Disposes everything allocated by this system.</summary>
        protected virtual void OnDestroy()
        {
            Runtime?.Dispose();
        }

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

        /// <inheritdoc/>
        public override IEnumerable<Transform> GetUsedBones()
            => _Colliders?.GetUsedBones();

        /************************************************************************************************************************/
#if UNITY_EDITOR
        /************************************************************************************************************************/

        /// <summary>Draws scene gizmos for this modifier.</summary>
        protected virtual void OnDrawGizmosSelected()
        {
            if (!enabled)
                return;

            Runtime?.DrawGizmos();
        }

        /************************************************************************************************************************/
#endif
        /************************************************************************************************************************/
    }
}
