namespace Zinnia.Process.Component { using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using Zinnia.Data.Type; /// /// An that runs a set method on each (or the first active) source collection against a collection of targets. /// public abstract class SourceTargetProcessor : MonoBehaviour, IProcessable where TEvent : UnityEvent, new() { /// /// Emitted if the value is going to change with the new value as the payload. /// public TEvent ActiveSourceChanging = new TEvent(); [Header("Process Settings")] [Tooltip("Whether to cease the processing of the source collection after the first valid source is processed.")] [SerializeField] private bool ceaseAfterFirstSourceProcessed = true; /// /// Whether to cease the processing of the source collection after the first valid source is processed. /// public bool CeaseAfterFirstSourceProcessed { get { return ceaseAfterFirstSourceProcessed; } set { ceaseAfterFirstSourceProcessed = value; } } /// /// The that is currently the active source for the process. /// public TSource ActiveSource { get; protected set; } /// /// Executes the relevant process to apply between the source and target. /// public abstract void Process(); /// /// Sets the current indices of the source and target collections. /// /// The source index. /// The target index. protected abstract void SetCurrentIndices(int sourceIndex, int targetIndex); /// /// Applies the source data to the target data. /// /// The source to apply the data from. /// The target to apply the data to. protected abstract void ApplySourceToTarget(TSource source, TTarget target); /// /// Determines if the given source is valid to process. /// /// The source to check. /// if the source is valid to process. protected virtual bool IsSourceValid(TSource source) { return !EqualityComparer.Default.Equals(source, default); } /// /// Determines if the given target is valid to process. /// /// The target to check. /// if the target is valid to process. protected virtual bool IsTargetValid(TTarget target) { return !EqualityComparer.Default.Equals(target, default); } /// /// Applies each (or the first active) source data to every (or only active) targets. /// /// The sources to apply the data from. /// The targets to apply the data to. protected virtual void ApplySourcesToTargets(HeapAllocationFreeReadOnlyList sources, HeapAllocationFreeReadOnlyList targets) { bool foundValidSource = false; for (int sourceIndex = 0; sourceIndex < sources.Count; sourceIndex++) { TSource currentSource = sources[sourceIndex]; if (!IsSourceValid(currentSource)) { continue; } for (int targetIndex = 0; targetIndex < targets.Count; targetIndex++) { TTarget currentTarget = targets[targetIndex]; if (!IsTargetValid(currentTarget)) { continue; } SetCurrentIndices(sourceIndex, targetIndex); ApplySourceToTarget(currentSource, currentTarget); } if (!EqualityComparer.Default.Equals(ActiveSource, currentSource)) { ActiveSourceChanging?.Invoke(currentSource); } ActiveSource = currentSource; foundValidSource = true; if (CeaseAfterFirstSourceProcessed) { break; } } if (!foundValidSource && !EqualityComparer.Default.Equals(ActiveSource, default)) { ActiveSource = default; ActiveSourceChanging?.Invoke(default); } } } }