using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using Artemis.Core.Events; namespace Artemis.Core; /// public abstract class Pin : CorePropertyChanged, IPin { #region Constructors /// /// Creates a new instance of the class on the provided node with the provided name /// /// The node the pin belongs to /// The name of the pin protected Pin(INode node, string name = "") { Node = node; _name = name; } #endregion /// public event EventHandler>? PinConnected; /// public event EventHandler>? PinDisconnected; #region Properties & Fields /// public INode Node { get; } /// public string Name { get => _name; set => SetAndNotify(ref _name, value); } private bool _isEvaluated; /// public bool IsEvaluated { get => _isEvaluated; set => SetAndNotify(ref _isEvaluated, value); } private bool _isNumeric; /// public bool IsNumeric { get => _isNumeric; protected set => SetAndNotify(ref _isNumeric, value); } private readonly List _connectedTo = new(); private string _name; /// public IReadOnlyList ConnectedTo => new ReadOnlyCollection(_connectedTo); /// public abstract PinDirection Direction { get; } /// public abstract Type Type { get; } /// public abstract object? PinValue { get; } #endregion #region Methods /// public void Reset() { IsEvaluated = false; } /// public void ConnectTo(IPin pin) { _connectedTo.Add(pin); if (!pin.ConnectedTo.Contains(this)) pin.ConnectTo(this); OnPropertyChanged(nameof(ConnectedTo)); PinConnected?.Invoke(this, new SingleValueEventArgs(pin)); } /// public void DisconnectFrom(IPin pin) { _connectedTo.Remove(pin); if (pin.ConnectedTo.Contains(this)) pin.DisconnectFrom(this); OnPropertyChanged(nameof(ConnectedTo)); PinDisconnected?.Invoke(this, new SingleValueEventArgs(pin)); } /// public void DisconnectAll() { if (!_connectedTo.Any()) return; List connectedPins = new(_connectedTo); _connectedTo.Clear(); foreach (IPin pin in connectedPins) { PinDisconnected?.Invoke(this, new SingleValueEventArgs(pin)); if (pin.ConnectedTo.Contains(this)) pin.DisconnectFrom(this); } OnPropertyChanged(nameof(ConnectedTo)); } /// public bool IsTypeCompatible(Type type, bool forgivingEnumMatching = true) { return Type == type || (Direction == PinDirection.Input && type.IsAssignableTo(Type)) || (Direction == PinDirection.Output && type.IsAssignableFrom(Type)) || (Direction == PinDirection.Input && Type == typeof(Enum) && type.IsEnum && forgivingEnumMatching) || (Direction == PinDirection.Output && type == typeof(Enum) && Type.IsEnum && forgivingEnumMatching); } /// /// Changes the type of this pin, disconnecting any pins that are incompatible with the new type. /// /// The new type of the pin. /// The backing field of the current type of the pin. protected void ChangeType(Type type, ref Type currentType) { if (currentType == type) return; bool changingEnums = type.IsEnum && currentType.IsEnum; List connections = new(ConnectedTo); DisconnectAll(); // Change the type SetAndNotify(ref currentType, type, nameof(Type)); IsNumeric = type == typeof(Numeric); foreach (IPin pin in connections.Where(p => p.IsTypeCompatible(type, changingEnums))) ConnectTo(pin); } #endregion }