classdef Input < bioinfo.pipeline.internal.SemanticModelObject
    

    properties (SetAccess = immutable)
        % REQUIRED is a logical scalar that indicates whether the input port is a
        % required input to the block. A required input must be connected,
        % provided as a runtime input to a pipeline, or have its 'Value'
        % property set to anything other than bioinfo.pipeline.datatypes.Unset;
        % otherwise the pipeline will fail to compile.
        %
        % This property is read-only.
        Required(1,1) logical
    end
    
    properties
        % VALUE allows the input port to be set to a specified value instead of
        % being connected or passed at runtime. The default value of
        % bioinfo.pipeline.datatypes.Unset indicates that no value is
        % provided, and the input will come from a connection or runtime
        % input. If an input port with a set value is also connected
        % to an output port of another block, the value coming from the
        % connection is used instead of Value.
        %
        % See also bioinfo.pipeline.datatypes.Unset, bioinfo.pipeline.Pipeline.run
        Value = bioinfo.pipeline.datatypes.Unset
        
        % SplitDimension controls how the input structure will be split across
        % multiple runs of a block in a pipeline. SplitDimension is a
        % vector of positive integers indicating which dimensions of the
        % input value to split, or "all" indicating that all elements of
        % the input should be passed to the run method of the block
        % independently. The default value is an empty row vector, meaning
        % that the value of the input is passed unchanged to the RUN method
        % of the block. If all of the SplitDimension of the input ports to
        % a block are empty, the block will run just once.
        % 
        % When a block has a single input with split dimensions, the
        % input value will be split in the corresponding dimensions before
        % being passed to the RUN method of the block. The number of times
        % the block is run is the product of the sizes of the input value in the
        % split dimensions.
        %
        % A block can have different split dimensions for each input, but
        % inputs that share split dimensions must have compatible size. As
        % with binary operations on MATLAB arrays, two inputs have
        % compatible size for a dimension if the size of the inputs is the
        % same or one of them is 1. For an input whose size is scalar (1)
        % in a split dimension, the value in that dimension is implicitly
        % expanded (scalar expansion) to match the same size as the other
        % dimensions.
        % 
        % % Example:
        %
        % import bioinfo.pipeline.blocks.UserFunction
        % p = bioinfo.pipeline.Pipeline;
        % 
        % sumBlock = addBlock(p, UserFunction(@sum, RequiredArguments="A", OutputArguments="S"));
        % inputStruct.A = [ 1 2 3; 4 5 6];
        % 
        % % With no split dimensions, equivalent to calling sum on
        % % entire input array, and one process is run
        % run(p, inputStruct);
        % results(p, sumBlock).S
        % processTable(p, 'Expanded', true)
        %
        % % By setting split dimension to 1, rows are treated
        % % independently. Each row is passed to the sumBlock and run in a
        % % different process.
        % 
        % sumBlock.Inputs.A.SplitDimension = 1;
        % run(p, inputStruct);
        % results(p, sumBlock).S
        % processTable(p, 'Expanded', true)
        %
        % See also bioinfo.pipeline.Pipeline.run
        SplitDimension {bioinfo.pipeline.internal.Validation.mustBeSplitDimension} = double.empty(1,0);
    end
    
    events
        PropertyValueChanged
    end
    
    methods
        function obj = Input(NVPArgs)
            % INPUT Create bioinfo.pipeline.Input object.
            %
            % I = INPUT(NAME) constructs an input with the given name.
            %
            % I = INPUT(..., 'Required', tf) indicates whether the port is a
            %     required input to the block.  A required input must be
            %     connected, provided as a runtime input to a pipeline, or
            %     have its 'Value' property set to anything other than
            %     bioinfo.pipeline.datatypes.Unset; otherwise the pipeline 
            %     will fail to compile. The default is true.
            %
            % See also Required, bioinfo.pipeline.Block
            arguments
                NVPArgs.Required(1,1) logical = true
            end
            
            obj.Required = NVPArgs.Required;
        end
        
        function set.SplitDimension(obj, sd)
            arguments
                obj(1,1) bioinfo.pipeline.Input
                sd {bioinfo.pipeline.internal.Validation.mustBeSplitDimension}
            end
            
            if isequal(sd, "all") || isequal(sd, 'all')
                obj.SplitDimension = "all";
            else
                % Convert to double, ensure row vector
                sd = double(sd);
                obj.SplitDimension = sd(:)';
            end
        end

        function set.Value(obj, value)
            arguments
                obj(1,1) bioinfo.pipeline.Input
                value
            end
            oldValue = obj.Value;
            shouldNotify = ~isequaln(value, obj.Value);
            obj.Value = value;
            if shouldNotify
                pvcEvt = bioinfo.pipeline.internal.PropertyValueChangedEvent("Value", oldValue, value);
                notify(obj, 'PropertyValueChanged', pvcEvt);
            end
        end
    end
end
% Copyright 2020-2022 The MathWorks, Inc.

