using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using Syntriax.Engine.Core.Abstract; using Syntriax.Engine.Core.Exceptions; namespace Syntriax.Engine.Core; [System.Diagnostics.DebuggerDisplay("Behaviour Count: {behaviours.Count}")] public class BehaviourController : IBehaviourController { public event IBehaviourController.OnPreUpdateEventHandler? OnPreUpdate = null; public event IBehaviourController.OnUpdateEventHandler? OnUpdate = null; public event IBehaviourController.OnPreDrawEventHandler? OnPreDraw = null; public event IBehaviourController.OnBehaviourAddedEventHandler? OnBehaviourAdded = null; public event IBehaviourController.OnBehaviourRemovedEventHandler? OnBehaviourRemoved = null; public event IAssignableGameObject.OnGameObjectAssignedEventHandler? OnGameObjectAssigned = null; public event IInitialize.OnInitializedEventHandler? OnInitialized = null; public event IInitialize.OnFinalizedEventHandler? OnFinalized = null; public event IAssignable.OnUnassignedEventHandler? OnUnassigned = null; private readonly IList behaviours = new List(Constants.BEHAVIOURS_SIZE_INITIAL); private IGameObject _gameObject = null!; private bool _initialized = false; public IGameObject GameObject => _gameObject; public bool IsInitialized { get => _initialized; private set { if (value == _initialized) return; _initialized = value; if (value) OnInitialized?.Invoke(this); else OnFinalized?.Invoke(this); } } public T AddBehaviour(T behaviour) where T : class, IBehaviour { InsertBehaviourByPriority(behaviour); behaviour.Assign(this); behaviour.Assign(GameObject.StateEnable); behaviour.Initialize(); behaviour.OnPriorityChanged += OnPriorityChange; OnBehaviourAdded?.Invoke(this, behaviour); return behaviour; } public T AddBehaviour(params object?[]? args) where T : class, IBehaviour => AddBehaviour(new Factory.BehaviourFactory().Instantiate(_gameObject, args)); public T? GetBehaviour() { foreach (var behaviourItem in behaviours) if (behaviourItem is T result) return result; return default; } public IList GetBehaviours() { List? behaviours = null; foreach (var behaviourItem in this.behaviours) { if (behaviourItem is not T behaviour) continue; behaviours ??= []; behaviours.Add(behaviour); } return behaviours ?? Enumerable.Empty().ToList(); } public void GetBehaviours(IList results) { results.Clear(); foreach (var behaviourItem in behaviours) { if (behaviourItem is not T behaviour) continue; results.Add(behaviour); } } public void RemoveBehaviour(bool removeAll = false) where T : class, IBehaviour { for (int i = behaviours.Count; i >= 0; i--) { if (behaviours[i] is not T behaviour) continue; RemoveBehaviour(behaviour); if (!removeAll) return; } } public void RemoveBehaviour(T behaviour) where T : class, IBehaviour { if (!behaviours.Contains(behaviour)) throw new Exception($"{behaviour.GetType().Name} does not exist in {GameObject.Name}'s {nameof(IBehaviourController)}."); behaviour.OnPriorityChanged -= OnPriorityChange; behaviour.Finalize(); behaviours.Remove(behaviour); OnBehaviourRemoved?.Invoke(this, behaviour); } public bool Assign(IGameObject gameObject) { if (GameObject is not null && GameObject.IsInitialized) return false; _gameObject = gameObject; OnGameObjectAssigned?.Invoke(this); return true; } public bool Initialize() { if (IsInitialized) return false; NotAssignedException.Check(this, _gameObject); foreach (IBehaviour behaviour in behaviours) behaviour.Initialize(); IsInitialized = true; return true; } public bool Finalize() { if (!IsInitialized) return false; foreach (IBehaviour behaviour in behaviours) behaviour.Finalize(); IsInitialized = false; return true; } public bool Unassign() { if (IsInitialized) return false; _gameObject = null!; OnUnassigned?.Invoke(this); return true; } public void Update() { if (!GameObject.StateEnable.Enabled) return; OnPreUpdate?.Invoke(this); OnUpdate?.Invoke(this); } public void UpdatePreDraw() { if (!GameObject.StateEnable.Enabled) return; OnPreDraw?.Invoke(this); } public BehaviourController() { } public BehaviourController(IGameObject gameObject) => Assign(gameObject); private void InsertBehaviourByPriority(T behaviour) where T : class, IBehaviour { int i; for (i = 0; i < behaviours.Count; i++) { if (behaviours[i].Priority > behaviour.Priority) continue; behaviours.Insert(i, behaviour); return; } if (i == 0 || i == behaviours.Count) behaviours.Add(behaviour); } private void OnPriorityChange(IBehaviour sender, int previousPriority) { behaviours.Remove(sender); InsertBehaviourByPriority(sender); } public IEnumerator GetEnumerator() => behaviours.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => behaviours.GetEnumerator(); }