using System.Collections.Generic; using System.Linq; namespace Unity.VisualScripting { public abstract class UnitPort : IUnitPort where TValidOther : IUnitPort where TInvalidOther : IUnitPort where TExternalConnection : IUnitConnection { protected UnitPort(string key) { Ensure.That(nameof(key)).IsNotNull(key); this.key = key; } public IUnit unit { get; set; } public string key { get; } public IGraph graph => unit?.graph; public IEnumerable relations => LinqUtility.Concat(unit.relations.WithSource(this), unit.relations.WithDestination(this)).Distinct(); public abstract IEnumerable validConnections { get; } public abstract IEnumerable invalidConnections { get; } public abstract IEnumerable validConnectedPorts { get; } public abstract IEnumerable invalidConnectedPorts { get; } IEnumerable IUnitPort.validConnections => validConnections.Cast(); public IEnumerable connections => LinqUtility.Concat(validConnections, invalidConnections); public IEnumerable connectedPorts => LinqUtility.Concat(validConnectedPorts, invalidConnectedPorts); public bool hasAnyConnection => hasValidConnection || hasInvalidConnection; // Allow for more efficient overrides public virtual bool hasValidConnection => validConnections.Any(); public virtual bool hasInvalidConnection => invalidConnections.Any(); private bool CanConnectTo(IUnitPort port) { Ensure.That(nameof(port)).IsNotNull(port); return unit != null && // We belong to a unit port.unit != null && // Port belongs to a unit port.unit != unit && // that is different than the current one port.unit.graph == unit.graph; // but is on the same graph. } public bool CanValidlyConnectTo(IUnitPort port) { return CanConnectTo(port) && port is TValidOther && CanConnectToValid((TValidOther)port); } public bool CanInvalidlyConnectTo(IUnitPort port) { return CanConnectTo(port) && port is TInvalidOther && CanConnectToInvalid((TInvalidOther)port); } public void ValidlyConnectTo(IUnitPort port) { Ensure.That(nameof(port)).IsNotNull(port); if (!(port is TValidOther)) { throw new InvalidConnectionException(); } ConnectToValid((TValidOther)port); } public void InvalidlyConnectTo(IUnitPort port) { Ensure.That(nameof(port)).IsNotNull(port); if (!(port is TInvalidOther)) { throw new InvalidConnectionException(); } ConnectToInvalid((TInvalidOther)port); } public void Disconnect() { while (validConnectedPorts.Any()) { DisconnectFromValid(validConnectedPorts.First()); } while (invalidConnectedPorts.Any()) { DisconnectFromInvalid(invalidConnectedPorts.First()); } } public abstract bool CanConnectToValid(TValidOther port); public bool CanConnectToInvalid(TInvalidOther port) { return true; } public abstract void ConnectToValid(TValidOther port); public abstract void ConnectToInvalid(TInvalidOther port); public abstract void DisconnectFromValid(TValidOther port); public abstract void DisconnectFromInvalid(TInvalidOther port); public abstract IUnitPort CompatiblePort(IUnit unit); protected void ConnectInvalid(IUnitOutputPort source, IUnitInputPort destination) { var connection = unit.graph.invalidConnections.SingleOrDefault(c => c.source == source && c.destination == destination); if (connection != null) { return; } unit.graph.invalidConnections.Add(new InvalidConnection(source, destination)); } protected void DisconnectInvalid(IUnitOutputPort source, IUnitInputPort destination) { var connection = unit.graph.invalidConnections.SingleOrDefault(c => c.source == source && c.destination == destination); if (connection == null) { return; } unit.graph.invalidConnections.Remove(connection); } } }