function [D,isDimSet,dim,omitnan,omitzero,isWeighted,w] = parseErrorMetricsInput(forMape,F,A,varargin)
% parseErrorMetricsInput Helper for error metrics functions
%
%   FOR INTERNAL USE ONLY -- This feature is intentionally undocumented.
%   Its behavior may change, or it may be removed in a future release.
%
%   MAPE(F,A)                        MAPE(F,A,dim)
%   MAPE(F,A,nanflag)                MAPE(F,A,dim,nanflag)
%   MAPE(F,A,zeroflag)               MAPE(F,A,dim,zeroflag)
%   MAPE(F,A,nanflag,zeroflag)       MAPE(F,A,dim,nanflag,zeroflag)
%   MAPE(F,A,zeroflag,nanflag)       MAPE(F,A,dim,zeroflag,nanflag)
%   MAPE(F,A,"Weight",W)             MAPE(F,A,dim,"Weight",W)
%   MAPE(F,A,nanflag,"Weight",W)     MAPE(F,A,dim,nanflag,"Weight",W)
%   MAPE(F,A,zeroflag,"Weight",W)    MAPE(F,A,dim,zeroflag,"Weight",W)
%   MAPE(F,A,zeroflag,nanflag,"Weight",W)
%   MAPE(F,A,nanflag,zeroflag,"Weight",W)
%   MAPE(F,A,dim,zeroflag,nanflag,"Weight",W)
%   MAPE(F,A,dim,nanflag,zeroflag,"Weight",W)
%
% RMSE has the same syntaxes as MAPE, except that it does not have the
% zeroflag.
%
%#codegen
%
%

% Copyright 2022-2023 The MathWorks, Inc.

coder.internal.prefer_const(forMape, varargin);
narginchk(2,inf);

coder.internal.errorIf(issparse(A) || issparse(F), 'Coder:toolbox:SparseNotSupported');
coder.internal.errorIf(~isfloat(A) || ~isfloat(F), 'MATLAB:errormetrics:InvalidData');
coder.internal.assertCompatibleDims(A,F);

D = A-F;

num = coder.const(numel(varargin));

% Parse dim
if nargin > 3 && coder.internal.isConst(varargin{1})
    possibleDim = varargin{1};
    %check all condition:
    isAll = coder.const(feval('matlab.internal.math.checkInputName', possibleDim, 'all'));

    isDimSet = coder.const(isnumeric(possibleDim) || isAll);

    if (isDimSet && ~isAll)
        coder.internal.assertValidDim(possibleDim);
    end
else
    isDimSet = false;
end

if isDimSet
    dim = varargin{1};
else
    dim = coder.internal.constNonSingletonDim(D);
    coder.internal.assert(coder.internal.isConst(size(D,dim)) || ...
    isscalar(D) || size(D,dim) ~= 1, ...
    'Coder:toolbox:autoDimIncompatibility');
end

% Parse flags and weight

if nargin == 3 || (nargin == 4 && isDimSet)
    % Syntax: mape(F,A) or mape(F,A,dim)
    isWeighted = false;
    omitnan = false;
    omitzero = false;
    w = [];
    return
end

indStart = coder.const(1 + isDimSet); %first index after dim
flag = varargin{indStart};
[isFlag,omitnan,omitzero,s] = checkFlag(flag,forMape);
isFlag = coder.const(isFlag);

if nargin == 4
    if isFlag
        % Syntax: mape(F,A,flag)
        w = [];
        isWeighted = false;
        return
    else
        coder.internal.errorIf(forMape, 'MATLAB:errormetrics:UnknownFlagMAPE');
        coder.internal.errorIf(true,'MATLAB:errormetrics:UnknownFlag');
    end
end

if isFlag
    indStart = coder.const(indStart + 1);
end

if nargin > 5 || ~isDimSet || ~isFlag
    if forMape && isFlag
        % mape can have two flags
        flag = varargin{indStart};
        [isFlag,omitnan2,omitzero2,s2] = checkFlag(flag,forMape);
        % Either both flags were nanflags or both were zeroflags
        duplicateNanFlags = (s(1) || s(2) || s(3) || s(4)) &&...
                            (s2(1) || s2(2) || s2(3) || s2(4));

        duplicateZeroFlags = (s(5) || s(6)) && (s2(5) || s2(6));
        coder.internal.errorIf(duplicateNanFlags || duplicateZeroFlags,...
            'MATLAB:errormetrics:UnknownFlagMAPE');
        omitnan = omitnan || omitnan2;
        omitzero = omitzero || omitzero2;
        if isFlag
            indStart = coder.const(indStart + 1);
        end
    end
    
    if rem(num-indStart,2) == 0
        coder.internal.errorIf(coder.const(feval('matlab.internal.math.checkInputName',varargin{end},'Weight')),...
            'MATLAB:errormetrics:KeyWithoutValue')
        coder.internal.errorIf(coder.const(feval('matlab.internal.math.checkInputName',varargin{indStart},'Weight')),...
            'MATLAB:errormetrics:InvalidPositionWeight')
        coder.internal.errorIf(forMape, 'MATLAB:errormetrics:UnknownFlagMAPE');
        coder.internal.errorIf(true, 'MATLAB:errormetrics:UnknownFlag');
    end

    % Parse any NV args
    wDefined = false;
    if indStart < num
        param = {'Weight'};
        poptions = struct( ...
            'CaseSensitivity',false, ...
            'PartialMatching','unique', ...
            'StructExpand',false, ...
            'IgnoreNulls',true);
        
        pstruct = coder.internal.parseParameterInputs(param,...
            poptions,varargin{indStart:end}); 
        
        coder.internal.errorIf(isempty(pstruct.Weight), 'MATLAB:errormetrics:KeyWithoutValue');
        
        wDefined = true;
    end
    
    if wDefined
        % Check value of inputted weight
        possibleW = coder.internal.getParameterValue(pstruct.Weight,[],varargin{indStart:end});
        
        coder.internal.errorIf(~isreal(possibleW) || ~isfloat(possibleW),...
            'MATLAB:errormetrics:InvalidWeight');
        
        invalidWeight = false;
        if omitnan
            for i = 1:numel(possibleW)
                if possibleW(i) < 0
                    invalidWeight = true;
                    break;
                end
            end
        else
            for i = 1:numel(possibleW)
                if possibleW(i) < 0 || isnan(possibleW(i))
                    invalidWeight = true;
                    break;
                end
            end
        end
        
        
        coder.internal.errorIf(invalidWeight,'MATLAB:errormetrics:InvalidWeight');

        coder.internal.errorIf(isDimSet && (isempty(dim) || ~isscalar(dim) || ischar(dim) || isstring(dim))...
            ,'MATLAB:errormetrics:WeightWithVecdim');
        if isvector(possibleW)
            coder.internal.assert(numel(possibleW) == size(D,dim),'MATLAB:errormetrics:InvalidSizeWeight');
        else
            coder.internal.errorIf(~(isequal(size(A),size(possibleW)) || isequal(size(F),size(possibleW)) ||...
                isequal(size(D),size(possibleW))) ,'MATLAB:errormetrics:InvalidSizeWeight');
        end

        if isvector(possibleW)
            % Reshape w to be applied in the direction dim
            sz = size(D);
            sz(end+1:dim) = 1;
            wresize = ones(size(sz));
            wresize(dim) = sz(dim);
            w = reshape(possibleW, wresize);
            if omitnan || omitzero
                % Repeat w, such that the new w has the same size as D
                wtile = sz;
                wtile(dim) = 1;
                w = repmat(w, wtile);
            end
        else
            w = possibleW;
        end

        if omitnan
            w(isnan(D)) = coder.internal.nan;
        end
        
        isWeighted = true;
    else
       isWeighted = false;
       w = [];
    end
else
    isWeighted = false;
    w = [];
end

end


%% Helper function
function [isFlag,omitnan,omitzero,s] = checkFlag(flag,forMape)
coder.internal.prefer_const(flag, forMape);

coder.internal.assert(coder.internal.isConst(flag), ...
    'Coder:toolbox:dimNotConst');

if forMape
    validFlags = {'omitnan', 'includenan', 'omitmissing','includemissing','omitzero', 'includezero'};
else
    validFlags = {'omitnan', 'includenan','omitmissing','includemissing'};
end

s = coder.const(feval('matlab.internal.math.checkInputName', flag, validFlags));
% Catch ambiguities, e.g. mape(F,A,'omit')
coder.internal.errorIf(forMape && sum(s) > 1,'MATLAB:errormetrics:UnknownFlagMAPE');
if forMape
    isFlag = s(1) || s(2) || s(3) || s(4) || s(5) || s(6);
    omitnan = s(1) || s(3);
    omitzero = s(5);
else
    isFlag = s(1) || s(2) || s(3) || s(4);
    omitnan = s(1) || s(3);
    omitzero = false;
end

if ~coder.internal.get_eml_option('NonFinitesSupport')
    omitnan = false;
end

end
