function validateattributes(a,classes,attributes,aFuncname,varname,argindex)
%MATLAB Code Generation Library Function

%   Limitations:
%   1. The 4th argument must be supplied and must be FUNCNAME.
%   2. Does not throw as caller.
%   3. Some error strings are simplified (e.g. with 'size' attribute).
%   4. CLASSES, FUNCNAME, VARNAME, and ARGINDEX must be constants.
%      Use coder.unroll if processing multiple inputs in a loop.
%   5. Non-constant numbers in error strings are formatted in a
%      full-precision, fixed-length format. For floats, this will be an
%      exponential format.

%   Copyright 1993-2018 The MathWorks, Inc.
%#codegen
%#ok<*EMCA>

eml_allow_enum_inputs;
eml_invariant(nargin >= 3,'MATLAB:narginchk:notEnoughInputs');
coder.internal.prefer_const(a,classes,attributes);
coder.internal.assert(isClassListInputType(classes), ...
    'Coder:toolbox:ValidateattributesBadClassList');
coder.internal.assert(isconstcell(classes), ...
    'Coder:toolbox:ValidateattributesNeedConstClasses');
coder.internal.assert(iscell(attributes), ...
    'MATLAB:validateattributes:badAttributeList');

if nargin < 4 || (nargin==4 && ~ischar(aFuncname))
    funcname = '';
else
    funcname = aFuncname;
end
coder.internal.prefer_const(funcname);
coder.internal.assert(eml_is_const(funcname), ...
    'Coder:toolbox:ValidateattributesNeedConstFunctionName');
if nargin < 5
    varname = '';
else
    coder.internal.prefer_const(varname);
    coder.internal.assert(eml_is_const(varname), ...
        'Coder:toolbox:ValidateattributesNeedConstVariableName');
    coder.internal.assert(ischar(varname), ...
        'Coder:toolbox:ValidateattributesBadVariableName');
end
if nargin < 6
    if nargin==4 && ~ischar(aFuncname)
        argindex = aFuncname;
        coder.internal.assert(eml_is_const(argindex), ...
            'Coder:toolbox:ValidateattributesNeedConstArgumentPosition');
        coder.internal.assert(coder.internal.isPosIntScalar(argindex), ...
            'MATLAB:validateattributes:badArgPosition');
    else
        argindex = 0;
    end
else
    coder.internal.assert(eml_is_const(argindex), ...
        'Coder:toolbox:ValidateattributesNeedConstArgumentPosition');
    coder.internal.assert(coder.internal.isPosIntScalar(argindex), ...
        'MATLAB:validateattributes:badArgPosition');
end

if coder.const(~isValidClass(a,classes))
    % Change pkg.foo to pkg:foo. We do this here to avoid the cost of inferring the replacement in the
    % passing case. The attributes handling code in coder.internal.validateattributesDispatcher
    % knows how to make a similar replacement more efficiently for attribute reporting.
    formattedFuncName = coder.const(feval('replace',funcname,'.',':'));
    coder.internal.assert(eml_ambiguous_types,...
        'CatalogID',...
        'MATLAB:validateattributes:invalidType',...
        'ReportedID',getReportedID(formattedFuncName,'invalidType'),...
        make_var_string(varname,argindex),...
        coder.const(make_classes_string(classes)), ...
        class(a));
end

coder.internal.validateattributesDispatcher(a,funcname,varname,argindex,attributes{:});

%--------------------------------------------------------------------------

function p = isValidClass(a,classes)
% Returns true iff class(a) is an element of the cell array classes.
coder.internal.prefer_const(a,classes);
eml_allow_enum_inputs;
p = false;
for k = coder.unroll(1:numel(classes))
    cls = classes{k};
    if isa(a,cls)
        p = true;
        break
    end
end

%--------------------------------------------------------------------------

function s = make_var_string(varname,argindex,capit)
% Make the string that substitutes for the variable name, i.e.
% "input", "input number %d", etc.
coder.extrinsic('num2str');
coder.internal.prefer_const(varname,argindex);
if coder.const(nargin < 3 || capit == false)
    input = 'input';
else
    input = 'Input';
end
if coder.const(isempty(varname))
    if coder.const(argindex < 1)
        s = input;
    else
        s = coder.const([input,' number ',coder.const(num2str(argindex))]);
    end
elseif coder.const(argindex < 1)
    s = coder.const(varname);
else
    s = coder.const([input,' number ',coder.const(num2str(argindex)),', ',varname,',']);
end

%--------------------------------------------------------------------------

function s = make_classes_string(classes)
% Make the constant classes string.
coder.internal.prefer_const(classes);
nclasses = numel(classes);
if coder.internal.isConst(nclasses) && coder.const(nclasses == 0)
    s = repmat(char(0),[1,0]);
else
    s = coder.nullcopy(eml_expand(char(0),[1,coder.const(get_classes_length(classes))]));
    offset = 0;
    for k = coder.unroll(1:nclasses)
        cls = classes{k};
        n = numel(cls);
        for j = 1:n
            s(j+offset) = cls(j);
        end
        offset = offset + n;
        if k < nclasses
            s(offset+1) = ',';
            s(offset+2) = ' ';
            offset = offset + 2;
        end
    end
end

%--------------------------------------------------------------------------

function n = get_classes_length(classes)
% Compute the length of the string listing the classes.
coder.internal.prefer_const(classes);
n = 0;
nclasses = numel(classes);
for k = coder.unroll(1:nclasses)
    cls = classes{k};
    n = n + numel(cls);
    if k < nclasses
        n = n + 2;
    end
end

%--------------------------------------------------------------------------

function p = isClassListInputType(x)
% Returns true iff x is a cell array of strings.
eml_allow_enum_inputs;
p = iscell(x);
if p
    for k = coder.unroll(1:numel(x))
        xk = x{k};
        if ~ischar(xk)
            p = false;
            return
        end
    end
end

%--------------------------------------------------------------------------

function p = isconstcell(x)
% Returns true iff x is a cell array of all constant elements.
coder.internal.prefer_const(x);
eml_allow_enum_inputs;
p = iscell(x);
if p
    for k = coder.unroll(1:numel(x))
        xk = x{k};
        if ~eml_is_const(xk)
            p = false;
            return
        end
    end
end

%--------------------------------------------------------------------------

function p = iscell(x)
% Should delete this when full cell support arrives.
eml_allow_enum_inputs;
p = isa(x,'cell') || isa(x,'coder.internal.cell');

%--------------------------------------------------------------------------

function repID = getReportedID(funcname, errorID)
coder.internal.prefer_const(funcname, errorID);
if isempty(funcname)
    repID = ['MATLAB:', errorID];
else
    repID = ['MATLAB:', funcname, ':', errorID];
end

%--------------------------------------------------------------------------
