classdef Connection < bioinfo.pipeline.internal.SemanticModelObject

   

    properties (SetAccess = private)
        % SourceBlock block at the source of the directed edge represented
        % by this Connection
        SourceBlock(:,1) bioinfo.pipeline.Block {mustBeScalarOrEmpty}
        
        % TargetBlock block at the target of the directed edge represented
        % by this connection
        TargetBlock(:,1) bioinfo.pipeline.Block {mustBeScalarOrEmpty}
    end

    % Note: define this property after SourceBlock & TargetBlock, so that
    % when Connection objects are loaded, the validation for PortMap
    % succeeds.
    properties
        %PORTMAP 1x2 string vector mapping an output port of the source
        %block to an input port of the target block in a pipeline.
        PortMap(:,2) string {bioinfo.pipeline.Connection.validatePortMap} = string.empty(0,2)
    end
    
    methods
        function obj = Connection(sourceBlock, targetBlock, portMap)
            arguments
                sourceBlock(:,1) bioinfo.pipeline.Block {mustBeScalarOrEmpty} = bioinfo.pipeline.Block.empty;
                targetBlock(:,1) bioinfo.pipeline.Block {mustBeScalarOrEmpty} = bioinfo.pipeline.Block.empty;
                portMap(1,2) string = ["Default", "Default"];
            end
            
            obj.SourceBlock = sourceBlock;
            obj.TargetBlock = targetBlock;
            
            if nargin > 2
                obj.PortMap = portMap;
            end
            
            if nargin > 0
                obj.validate();
            end
            
        end
        
        function set.PortMap(obj, portMap)
            arguments
                obj(1,1) bioinfo.pipeline.Connection
                portMap(1,2) string {bioinfo.pipeline.Connection.validatePortMap}
            end
            
            % Validate first, so the port map is not assigned if it is
            % invalid
            validate(obj, portMap);
            
            obj.PortMap = portMap;
        end
    end
    
    methods (Access = private)
        function validate(obj, portMap)
            %VALIDATE ensure PortMap is valid for the Source & Target
            % Blocks
            
            if nargin == 1
                portMap = obj.PortMap;
            end
            if obj.SourceBlock == obj.TargetBlock
                error(message("bioinfo:Connection:NoSelfConnections"));
            end
            
            if ~isfield(obj.SourceBlock.Outputs, portMap(1))
                error(message("bioinfo:Connection:InvalidPortMapOutput"));
            end
            
            if ~isfield(obj.TargetBlock.Inputs, portMap(2))
                error(message("bioinfo:Connection:InvalidPortMapInput"));
            end
        end
    end
    
    methods (Static, Access = private)
        function validatePortMap(portMap)
            %VALIDATEPORTMAP validates the port map property
            
            % PortMap must be 0x2 or 1x2 string array
            
            nRows = size(portMap, 1);
            if nRows > 1 || nRows < 0
                error('bioinfo:Connection:InvalidPortMapSize', 'PortMap must be a 1x2 string vector');
            end
            
        end
    end
end

% Copyright 2020-2021 The MathWorks, Inc.

