function [argOutCell, exception] = sBuildArgChk( buildfcn, varargin )
;%#ok undocumented

% Copyright 2008-2023 The MathWorks, Inc.

% Static method of distributed.

% We will modify argsList as we go through, and then return it.
argsList = varargin;
exception = []; % Return an exception if necessary
argOutCell = []; % Only filled out if successful

% distributed is disabled on ThreadPool, i.e., if a ThreadPool is already
% open or if one is about to auto-open.
if parallel.internal.pool.isThreadsDefaultPool()
    exception = MException(message("parallel:lang:pool:DisallowedParallelFeature", "distributed"));
    return;
end

% Furthermore, distributed is not supported on local process pools running on Apple silicon Macs.
if strcmp(computer, 'MACA64') && parallel.internal.pool.isDefaultPoolLocalProcesses()
    error(message("parallel:distributed:CodistributedDistributedUnsupportedOnMaca", "distributed"));
end

% Lists of acceptable class arguments
floatClasses = {'double', 'single'};
intClasses32 = {'int8', 'uint8', 'int16', 'uint16', 'int32', 'uint32'};
intClasses64 = {'int64', 'uint64'};
allClasses   = [floatClasses, intClasses32, intClasses64];
allPlusLogical = [allClasses, 'logical'];

% Control parameters
classList              = allPlusLogical; % In what follows, an empty classList
                                         % means that it is invalid to request
                                         % a specific class of output by passing
                                         % a class string as input to the build
                                         % function.
                                     
minSizeTypeArgs        = 0; % Zeros can be called with no inputs, and the result
                            % will be the scalar 0. This is documented, so we 
                            % strive to replicate that behavior here.
maxSizeTypeArgs        = Inf;
allowSingleSizeVecArg  = true;
maxNDims               = Inf; % Only for single vector arg

firstSizeArg = 1;
lastSizeArg  = length( argsList );

switch buildfcn
  case {'true', 'false'}
    classList = {'logical'};
  case { 'cell'}
    classList             = {};
  case {'ones', 'zeros', 'empty'}
    % Defaults are designed for ones/zeros
  case {'eye'}
    maxSizeTypeArgs       = 2;
    maxNDims              = 2;
  case {'inf', 'Inf', 'nan', 'NaN', 'rand', 'randn'}
    classList             = floatClasses;
  case {'colon', 'linspace', 'logspace'}
    classList = {};
    minSizeTypeArgs       = 2;
    maxSizeTypeArgs       = 3;
    allowSingleSizeVecArg = false;
  case {'randi'}
    % Special case - remove first arg and check separately, let other args flow through
    if nargin < 2
        exception = MException(message('parallel:distributed:BuildNargChck'));
        return;
    end
    [val, E]              = iCheckSizeTypeArg( buildfcn, argsList{1}, 1, 2 );
    if ~isempty( E )
        throwAsCaller( E );
    else
        argsList{1}       = val;
    end
    firstSizeArg          = 2;
    classList             = [floatClasses intClasses32 'logical'];
    % Rest of defaults work OK here.
  case {'spalloc'}
    classList             = {};
    maxSizeTypeArgs       = 3;
    minSizeTypeArgs       = 3;
    allowSingleSizeVecArg = false;
  case {'speye'}
    classList             = {};
    minSizeTypeArgs       = 1;
    maxSizeTypeArgs       = 2;
    maxNDims              = 2;
  case {'sprand', 'sprandn'}
    classList             = {};
    % Distributed only supports the three input argument form of these functions.
    minSizeTypeArgs       = 3; % density is "size-type".
    maxSizeTypeArgs       = 3;
    allowSingleSizeVecArg = false;
  case {'sprandsym'}
    classList             = {};
    % Distributed only supports the two input argument (n, density) form of this functions.
    minSizeTypeArgs       = 2; % density is "size-type".
    maxSizeTypeArgs       = 2;
    allowSingleSizeVecArg = false;
  otherwise
    exception = MException(message('parallel:distributed:BuildNotImplemented', ...
                                   buildfcn));
    return;
end

if isempty(argsList)
    % an empty argList is valid
    isValidArgList = true;
else
    % Ensure that argsList is a cell array of leading size-type
    % arguments, ...
    isValidArgList = all(cellfun(@(x) isnumeric(x) || islogical(x),...
                             argsList(1:end-1)));    
    % and optionally a trailing classname. 
    isValidArgList  = isValidArgList && ...
        (isnumeric(argsList{end}) || islogical(argsList{end}) || ...
        (~isempty(classList) && matlab.internal.datatypes.isScalarText(argsList{end})));
end

if ~isValidArgList
    if isempty(classList)
        exception = MException(...
            message('parallel:distributed:BuildInvalidArgumentType', buildfcn));
    else
        exception = MException(...
            message('parallel:distributed:BuildInvalidArgumentTypeWithClass', ...
                    buildfcn));
    end
    return;
end

% By the time we get here, argsList is a cell array of leading size-type
% arguments, and optionally a trailing classname. Deal with that first.
if length( argsList ) >= 1 ...
        && iIsScalarString( argsList{end} )
    % Got a classname
    if ismember( argsList{end}, classList )
        % OK
    else
        exception = MException(message('parallel:distributed:BuildBadClassName', ...
                                       buildfcn, argsList{end}));
        return;
    end
    lastSizeArg = length( argsList ) - 1;
end


if allowSingleSizeVecArg
    checkLength = maxNDims; % the first size-type arg
else
    checkLength = 1;
end
for ii = firstSizeArg : lastSizeArg
    [val, E] = iCheckSizeTypeArg( buildfcn, argsList{ii}, 1, checkLength );
    if ~isempty( E )
        exception = E;
        return;
    else
        argsList{ii} = val;
    end
    checkLength = 1;
end

numSizeTypeArgs = 1 + (lastSizeArg - firstSizeArg);
if firstSizeArg <= lastSizeArg
    % We've actually got some size-type arguments, so check that if we've got a
    % size-vector as the first argument, that we haven't got any other
    % size-type arguments.
    if numel( argsList{firstSizeArg} ) > 1
        if numSizeTypeArgs == 1
            % Ok - just one size-type argument
        else
            exception = MException(...
                message('parallel:distributed:BuildInvalidSizeArguments',...
                        buildfcn));
            return;
        end
    end
end

% Finally, check that we've got an allowed number of size-type arguments
if numSizeTypeArgs < minSizeTypeArgs || numSizeTypeArgs > maxSizeTypeArgs
    if minSizeTypeArgs == maxSizeTypeArgs
        exception = MException(...
            message('parallel:distributed:BuildInvalidNumberOfSizesExact', ...
                    numSizeTypeArgs, buildfcn, buildfcn, minSizeTypeArgs));
    else
        exception = MException(...
            message('parallel:distributed:BuildInvalidNumberOfSizesRange', ...
                    numSizeTypeArgs, buildfcn, buildfcn, ...
                    minSizeTypeArgs, maxSizeTypeArgs));
    end
    return;
end

% Return the modified argsList.
argOutCell = argsList;
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [val, E] = iCheckSizeTypeArg( buildfcn, val, minLen, maxLen )
szVal = size( val );
ndVal = ndims( val );
E     = [];

% [] is treated as 0
if isequal(val,[])
    val = 0;
    return
end

% Only allow scalars or vectors
if (ndVal > 2) || ...
        (min( szVal ) ~= 1)
    E = MException(message('parallel:distributed:BuildArgScalarVector', buildfcn));
    return;
end

val = gather( val );

% Ok, it's a scalar or vector, is the length ok?
if length( val ) <= maxLen && length( val ) >= minLen
    % Ok
else
    E = MException(message('parallel:distributed:BuildArgInvalidSize', ...
                    szVal(1), szVal(2), buildfcn ));
    return;
end

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function tf = iIsScalarString( s )
tf = ischar(s) || (isstring(s) && isscalar(s));
end