using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
namespace Awperative;
///
/// Base class for all Awperative Entities. Responsible for Managing hierarchy between Components and Scenes, has Extensive Component Manipulation Available.
/// Also transfers Time and Carries most of the responsibilities akin to the Component.
///
/// Please don't inherit this. I don't know why you would
/// Avery Norris
public abstract class ComponentDocker
{
///
/// List of all Components belonging to the Docker, Please Use Add, Get, Move and Destroy to modify it.
///
public ImmutableArray Components => [.._Components];
///
/// Amount of all Components in the Docker
///
public int Count => _Components.Count;
///
/// Core of Docker, contains all of our precious Components. Sorts them by their priorities with highest going first.
/// If they are equal it defaults to hash codes to ensure consistent Behavior
///
internal SortedSet _Components = new(_componentSorter);
///
/// How Priority is sorted.
///
private readonly static Comparer _componentSorter = Comparer.Create((a, b) => {
int result = b.Priority.CompareTo(a.Priority);
return (result != 0) ? result : a.GetHashCode().CompareTo(b.GetHashCode());
});
///
/// Resorts member of Component list to match the Priority.
///
/// Component to modify
/// New priority for Component
internal void UpdatePriority(Component __component, int __priority) {
foreach (String tag in __component._tags) {
_taggedComponents[tag].Remove(__component);
} _Components.Remove(__component);
__component._priority = __priority;
foreach (String tag in __component._tags) {
_taggedComponents[tag].Add(__component);
} _Components.Add(__component);
}
///
/// Called by Awperative when the game is Closed, sends the event to all children; and they send it to their children.
///
/// Will not always trigger if the program is force closed
internal virtual void ChainUnload() { foreach (Component component in (Component[])[.._Components]) { if(component.Enabled) { component.Unload(); component.ChainUnload(); } } }
///
/// Called by Awperative when the game is Opened, sends the event to all children; and they send it to their children.
///
internal virtual void ChainLoad() { foreach (Component component in (Component[])[.._Components]) { if(component.Enabled) { component.Load(); component.ChainLoad(); } } }
///
/// Called by Awperative when the game is Updated sends the event to all children; and they send it to their children.
///
internal virtual void ChainUpdate() { foreach (Component component in (Component[])[.._Components]) { if(component.Enabled) { component.Update(); component.ChainUpdate(); } } }
///
/// Called by Awperative when the game is Drawn, sends the event to all children; and they send it to their children.
///
/// Only use this method for drawing methods
internal virtual void ChainDraw() { foreach (Component component in (Component[])[.._Components]) { if(component.Enabled) { component.Draw(); component.ChainDraw(); } } }
///
/// Called by Awperative when this is Created, sends the event to all children; and they send it to their children.
///
internal virtual void ChainCreate() { foreach (Component component in (Component[])[.._Components]) { if(component.Enabled) { component.Create(); component.ChainCreate(); } } }
///
/// Called by Awperative when this Component is destroyed, sends the event to all children; since they will be Destroyed too. And they send it to their children.
///
/// Not called when the game is closed
internal virtual void ChainDestroy() { foreach(Component component in (Component[])[.._Components]) { if(component.Enabled) { component.Destroy(); component.ChainDestroy(); } } }
///
/// Add a new Component to the Docker; This is the only way they should be created.
///
/// Arguments to construct the Component with
/// Type of Component to instantiate
///
public __Type Add<__Type>(object[] __args) where __Type : Component {
//Component does not have a constructor that matches the given args
if (typeof(__Type).GetConstructor(__args.Select(x => x.GetType()).ToArray()) == null) {
Debug.LogError("Component cannot be constructed with the given arguments",
["Type", "Args"],
[typeof(__Type).ToString(), "[" + string.Join(", ", __args.Select(x => x.ToString())) + "]"]); return null;
};
Component newComponent;
//Tries to instantiate Component, and sends a fail call if an issue occurs.
try { newComponent = (__Type)Activator.CreateInstance(typeof(__Type), __args); }
catch {
Debug.LogError("Component creation failed!", ["Type", "Args", "Docker"],
[typeof(__Type).ToString(), "[" + string.Join(", ", __args.Select(x => x.ToString())) + "]", GetHashCode().ToString()]); return null;
}
//If Component is null do not add
if(newComponent == null) {
Debug.LogError("Activator created Null Component", ["Type", "Args", "Docker"],
[typeof(__Type).ToString(), "[" + string.Join(", ", __args.Select(x => x.ToString())) + "]", GetHashCode().ToString()]); return null;
}
//Add to docker and initialize the new Component
_Components.Add(newComponent);
newComponent.Initiate(this);
return (__Type) newComponent;
}
///
/// Adds a new Component to the Docker; This is the only way they should be created.
///
///
///
public __Type Add<__Type>() where __Type : Component => Add<__Type>([]);
///
/// Transfers a child Component to another Docker
///
/// Component to move
/// Docker to move component to
/// Components cannot transfer themselves with this Method!
public void Move(Component __component, ComponentDocker __componentDocker) {
//This allows self transfer behavior while preserving Docker's actual job, Before all other statements to prevent Double-Debugging.
if (__component == this) { __component.ComponentDocker.Move(__component, __componentDocker); return; }
if (__component == null) {
Debug.LogError("Component is null!", ["CurrentDocker", "NewDocker"],
[GetHashCode().ToString(), __componentDocker.GetHashCode().ToString()]); return; }
if (!_Components.Contains(__component)) {
Debug.LogError("Docker does not have ownership of Component", ["Component", "Type", "CurrentDocker", "NewDocker"],
[__component.GetHashCode().ToString(), __component.GetType().ToString(), GetHashCode().ToString(), __componentDocker.GetHashCode().ToString()]); return; }
//Update docker lists
__componentDocker._Components.Add(__component);
_Components.Remove(__component);
//Update components parent
__component.ComponentDocker = __componentDocker;
}
///
/// Transfers the first found Component of a specific type to another Docker
///
/// Docker to move component to
/// Type of component
public void Move<__Type>(ComponentDocker __componentDocker) where __Type : Component => Move(Get<__Type>(), __componentDocker);
///
/// Transfers all Components in a list to another Docker.
///
/// List of Components to transfer
/// Docker to move Component to
public void MoveAll(IEnumerable __Components, ComponentDocker __componentDocker) { foreach (Component Component in (Component[])[..__Components]) Move(Component, __componentDocker); }
///
/// Transfers all Components of a type to another Docker.
///
/// Target Docker
/// Type of Components to transfer
public void MoveAll<__Type>(ComponentDocker __componentDocker) where __Type : Component => MoveAll(GetAll<__Type>(), __componentDocker);
/// ///
/// Holds Components at Keys of their tags.
///
internal Dictionary> _taggedComponents = [];
///
/// Add component to its proper place in the dictionary and resort values to match priorities.
///
/// Component to hash
/// Value to try and hash
internal void HashTaggedComponent(Component __component, string __tag) {
if (!__component._tags.Add(__tag)) {
Debug.LogError("Component already has tag!", ["Component", "Type", "Tag", "Docker"],
[__component.GetHashCode().ToString(), __component.GetType().ToString(), __tag, GetHashCode().ToString()]); return;
}
if (_taggedComponents.TryGetValue(__tag, out SortedSet components)) {
components.Add(__component);
} else { _taggedComponents.Add(__tag, new SortedSet(_componentSorter)); _taggedComponents[__tag].Add(__component); }
}
///
///
///
///
///
internal void UnhashTaggedComponent(Component __component, string __tag) {
if (!__component._tags.Remove(__tag)) {
Debug.LogError("Component already doesn't have that tag!", ["Component", "Type", "Tag", "Docker"],
[__component.GetHashCode().ToString(), __component.GetType().ToString(), __tag, GetHashCode().ToString()]); return;
}
if (_taggedComponents.TryGetValue(__tag, out SortedSet components)) {
components.Remove(__component);
if(components.Count == 0)
_taggedComponents.Remove(__tag);
}
}
///
/// Finds the first instance of a component with a given tag
///
///
///
internal Component Get(string __tag) {
if (_taggedComponents.TryGetValue(__tag, out SortedSet components))
return ((Component[])[..components])[0];
return null;
}
///
/// Finds all Components with a given tag
///
///
///
internal ImmutableArray GetAll(string __tag) {
if (_taggedComponents.TryGetValue(__tag, out SortedSet components))
return [..components];
return [];
}
///
/// Finds the first Component that has all the given tags
///
///
///
internal Component Get(List __tags) { ImmutableArray returnValue = GetAll(__tags); return returnValue.Length > 0 ? returnValue[0] : null; }
///
/// Finds all Components that have all the given tags
///
///
///
internal ImmutableArray GetAll(List __tags) {
if (__tags.Count == 0)
return [];
SortedSet foundComponents = _taggedComponents[__tags[0]];
for (int i = 1; i < __tags.Count; i++) {
foreach (Component component in (Component[])[..foundComponents]) {
if (!_taggedComponents[__tags[i]].Contains(component)) foundComponents.Remove(component);
}
}
return [..foundComponents];
}
///
/// Searches and returns the first Component of a type found on the Docker.
///
/// The Type of Component to search for
///
public __Type Get<__Type>() where __Type : Component {
//Iterates through the loop and returns if a match is found
foreach (Component component in (Component[])[.._Components])
if (component is __Type foundComponent) return foundComponent;
//Throws error if there is no Component found
Debug.LogError("Docker does not have target Component", ["Type", "Docker"],
[typeof(__Type).ToString(), GetHashCode().ToString()]); return null;
}
///
/// Searches and returns all Components of a type found on the Docker.
///
/// The Type of Components to search for
///
public ImmutableArray<__Type> GetAll<__Type>() where __Type : Component {
List<__Type> foundComponents = [];
//Iterates through the loop and returns if a match is found
foreach (Component component in (Component[])[.._Components]) {
if (component is __Type foundComponent) {
foundComponents.Add(foundComponent);
}
}
//Throws error if there is no Component found
if (foundComponents.Count == 0) {
Debug.LogError("Docker does not have target Component", ["Type", "Docker"],
[typeof(__Type).ToString(), GetHashCode().ToString()]);
return [];
}
return [..foundComponents];
}
///
/// Returns a bool based on if the Docker contains a Component with the given tag or not
///
///
///
public bool Contains(string __tag) => _taggedComponents.ContainsKey(__tag);
///
/// Returns a bool based on if the Docker contains a Component of that type or not
///
/// Type of the Component
///
public bool Contains<__Type>() where __Type : Component => _Components.Any(x => x is __Type);
///
/// Returns a bool based on if the current __Component is owned by this Docker
///
///
///
public bool Contains(Component __component) => _Components.Contains(__component);
///
/// Destroys a Component attached to docker
///
///
public void Remove(Component __component) {
//Component is null
if (__component == null) {
Debug.LogError("Component is null", ["CurrentDocker"],
[GetHashCode().ToString()]); return;
}
//Docker doesn't have Component
if (!_Components.Contains(__component)) {
Debug.LogError("Docker does not have ownership of Component", ["Component", "Type", "CurrentDocker"],
[__component.GetHashCode().ToString(), __component.GetType().ToString(), GetHashCode().ToString()]); return;
}
__component.Destroy();
__component.ChainDestroy();
foreach (string tag in __component._tags) UnhashTaggedComponent(__component, tag);
__component.ComponentDocker = null;
_Components.Remove(__component);
}
///
/// Destroys the first found Component of a given type
///
/// Type of Component to destroy
public void Remove<__Type>() where __Type : Component => Remove(Get<__Type>());
///
/// Destroys all Components from a given collection.
///
///
public void RemoveAll(IEnumerable __Components) { foreach (Component component in (Component[])[..__Components]) { Remove(component); } }
///
/// Destroys all Components of a given type
///
///
public void RemoveAll<__Type>() where __Type : Component => RemoveAll(GetAll<__Type>());
///
/// Destroys all Components attached to Docker
///
public void RemoveAll() { foreach (Component component in (Component[])[.._Components]) { Remove(component); } }
}