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

using System.Collections.Generic;
using UnityEngine;

namespace FlexiMotion.Modifiers
{

    /// <summary>A component which modifies the behaviour of <see cref="FlexiMotionComponent"/>.</summary>
    /// <remarks>
    /// Modifiers don't need to inherit from this class, it just helps simplify the initialization and cleanup.
    /// The actual modifying functionality is implemented via the interfaces in
    /// <see cref="JobModifierGroup"/>.
    /// </remarks>
    /// https://kybernetik.com.au/flexi-motion/api/FlexiMotion.Modifiers/FlexiMotionModifier
    /// 
    [HelpURL(FMStrings.DocsURLs.ModifiersAPIDocumentation + "/" + nameof(FlexiMotionModifier))]
    public abstract class FlexiMotionModifier : MonoBehaviour,
        IFlexiMotionModifier,
        IOnValidate
    {
        /************************************************************************************************************************/

        /// <summary>The name of the serialized backing field of <see cref="Target"/>.</summary>
        public const string
            TargetFieldName = nameof(_Target);

        [SerializeField, Tooltip("The simulation that will be modified")]
        private FlexiMotionComponent _Target;

        /// <inheritdoc/>
        public FlexiMotionComponent Target
        {
            get => _Target;
            set
            {
                var wasEnabled = _Target != null && Disable(_Target.Modifiers);

                _Target = value;

                if (wasEnabled && _Target != null)
                    Enable(_Target.Modifiers);
            }
        }

        /// <inheritdoc/>
        IFlexiMotionComponent IFlexiMotionModifier.Target => _Target;

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

        /// <summary>The <see cref="FlexiMotionRuntime.DeltaTime"/>.</summary>
        public float DeltaTime => Target.Runtime.DeltaTime;

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

        /// <summary>Tries to find a <see cref="Target"/> if none was assigned.</summary>
        protected virtual void OnValidate()
        {
            if (_Target == null)
                TryGetComponent(out _Target);
        }

        void IOnValidate.OnValidate()
            => OnValidate();

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

        /// <summary>Calls <see cref="Enable"/> if the <see cref="Target"/> is set.</summary>
        protected virtual void OnEnable()
        {
            if (_Target != null)
                Enable(_Target.Modifiers);
        }

        /// <summary>Registers this modifier in the given <see cref="JobModifierGroup"/>.</summary>
        protected virtual void Enable(JobModifierGroup modifiers) { }

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

        /// <summary>Calls <see cref="Disable"/> if the <see cref="Target"/> is set.</summary>
        protected virtual void OnDisable()
        {
            if (_Target != null)
                Disable(_Target.Modifiers);
        }

        /// <summary>Registers this modifier in the given <see cref="JobModifierGroup"/>.</summary>
        protected virtual bool Disable(JobModifierGroup modifiers)
            => false;

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

        /// <inheritdoc/>
        public virtual IEnumerable<Transform> GetUsedBones()
            => null;

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