function varargout = subsref( obj, S )
%SUBSREF Composite subsref method retrieves remote values
%   B = C(I) returns the entry of Composite C from lab I as a cell array
%   B = C([I1, I2, ...]) returns multiple entries as a cell array
%
%   B = C{I} returns a single entry
%   [B1, B2, ...] = C{[I1, I2, ...]} returns multiple entries

% Copyright 2008-2022 The MathWorks, Inc.

if strcmp( S(1).type, '.' )
    error(message('parallel:lang:spmd:CompositeSubsrefNoDotRef'));
end

% Error early in the case where the pool is closed
if ~obj.isResourceSetOpen()
    error(message('parallel:lang:spmd:CompositeSubsrefInvalid'));
end

% First dispatch the subsref on the Composite.
intermediateOutputs = iDispatchFirstLevel(obj, S(1));
numOut = max(1,nargout);
varargout = cell(1,numOut);

% If more levels of indexing were requested then apply them to the
% output.
if numel(S)>1
    % Make sure we match MATLAB's error for too many intermediate
    % results.
    if numel(intermediateOutputs) > 1
        error(message("MATLAB:index:expected_one_output_from_intermediate_indexing", ...
            getString(message("MATLAB:index:brace_index")), numel(intermediateOutputs)));
    end
    % Re-dispatch subsref for the rest
    [varargout{:}] = subsref(intermediateOutputs{1}, S(2:end));
else
    % Copy the final output into the return arguments.
    varargout = intermediateOutputs(1:numOut);
end
end


function out = iDispatchFirstLevel(obj, S)
switch S.type
    case '()'
        if nargout > 1
            error(message('parallel:lang:spmd:CompositeSubsrefNargout', nargout));
        end

        try
            % Defer to builtin indexing of the key vector
            keyHolderCell = obj.KeyVector( S.subs{:} );
            ret           = cell( size( keyHolderCell ) );

            % Check before actually making the remote call
            labs = iCheckValuesExist( obj, S.subs{:} );

            for ii=1:length( keyHolderCell )
                ret{ii} = obj.getValOrError( keyHolderCell{ii}, labs(ii) );
            end
            out{1}  = ret;
        catch E
            EE = MException(message('parallel:lang:spmd:CompositeSubsrefInvalidRequest'));
            EE = addCause( EE, E );
            throw( EE );
        end
    case '{}'
        try
            % Use special NUMEL syntax to work out how many outputs to
            % produce (this may be different to how many were requested!)
            numOut = numArgumentsFromSubscript(obj.KeyVector, S, ...
                matlab.indexing.IndexingContext.Expression);

            % Defer to builtin indexing of the key vector
            keyHolderCell = cell( 1, numOut );
            out           = cell( 1, numOut );
            [keyHolderCell{1:numOut}] = obj.KeyVector{ S.subs{:} };

            % Check before actually making the remote call
            labs = iCheckValuesExist( obj, S.subs{:} );

            for ii=1:length( keyHolderCell )
                out{ii} = obj.getValOrError( keyHolderCell{ii}, labs(ii) );
            end
        catch E
            EE = MException(message('parallel:lang:spmd:CompositeSubsrefInvalidRequest'));
            EE = addCause( EE, E );
            throw( EE );
        end

    otherwise
        % Never get here - '.' indexing already handled.
        error(message('parallel:lang:spmd:CompositeSubsrefUnexpectedRequest'));
end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% iCheckValuesExist - use Composite.exist to check before we attempt to
% retrieve things whether the values exist
function labsRequested = iCheckValuesExist( obj, varargin )
allLabs       = 1:length( obj );
labsRequested = allLabs( varargin{:} );

% See if any labs don't have a value
labsNoValue   = labsRequested( ~exist( obj, labsRequested ) );
if ~isempty( labsNoValue )
    error(message('parallel:lang:spmd:CompositeSubsref', sprintf( '%d ', labsNoValue )));
end
end
