function [b,tf] = rmmissing(a,varargin)
%MATLAB Code Generation Library Function

%   Limitations:
%   Requires variable-sizing
%   Sparse inputs are not supported

%   Copyright 2018-2023 The MathWorks, Inc.
%#codegen


coder.internal.prefer_const(varargin);
narginchk(1,inf);
coder.internal.assert(ismatrix(a),'MATLAB:rmmissing:NDArrays');
coder.internal.assert(~issparse(a), 'Coder:toolbox:SparseNotSupported',mfilename);

nargs = coder.internal.indexInt(length(varargin));
ZERO = coder.internal.indexInt(0);
ONE = coder.internal.indexInt(1);
TWO = coder.internal.indexInt(2);
nextArg = ONE;
lastArg = nargs;
ISTABLEORTIMETABLE = istabular(a);

% Parse the DIM and N-V pairs
minnmidx = ZERO;
dataVarsIdx = ZERO;
if nargs >= 1 && ~(ischar(varargin{nextArg}) || isstring(varargin{nextArg}))
    %   rmmissing(A,DIM,N1,V1,N2,V2,...)
    dim = coder.internal.indexInt(varargin{nextArg});
    coder.internal.assert(coder.internal.isConst(dim),'Coder:toolbox:DimMustBeConstant');
    coder.internal.assertValidDim(dim);
    coder.internal.assert(dim == coder.internal.indexInt(1) || ...
        dim == coder.internal.indexInt(2),'MATLAB:rmmissing:DimensionInvalid');
    nextArg = TWO;
else
    if ISTABLEORTIMETABLE
        dim = ONE;
    else
        dim = coder.internal.constNonSingletonDim(a);
        coder.internal.assert(isscalar(a) || size(a,dim) ~= 1, ...
            'Coder:toolbox:autoDimIncompatibility');
    end
end

coder.internal.assert(~ISTABLEORTIMETABLE || dim == ONE, 'MATLAB:rmmissing:TableDimCodegen');

coder.internal.assert(eml_bitand(lastArg - nextArg + ONE,ONE) == ZERO, ...
    'MATLAB:rmmissing:NameValuePairs');

for k = coder.unroll(nextArg:TWO:lastArg - ONE)
    coder.internal.assert(coder.internal.isConst(varargin{k}), ...
        'Coder:toolbox:OptionStringsMustBeConstant');
    name = validatestring(varargin{k},{'MinNumMissing','DataVariables'}, ...
        'rmmissing','',k + 1);
    coder.internal.assert(~strcmp(name,'DataVariables') || ISTABLEORTIMETABLE, ...
        'MATLAB:rmmissing:DataVariablesArray');
    if strcmp(name,'MinNumMissing')
        minnmidx = k + 1;
    elseif strcmp(name,'DataVariables')
        dataVarsIdx = k + 1;
    end
end

MINNUMMISSING_SUPPLIED = minnmidx > ZERO;
DATAVARS_SUPPLIED = dataVarsIdx > ZERO;
if MINNUMMISSING_SUPPLIED
    validateattributes(varargin{minnmidx},{'numeric'}, ...
        {'scalar','positive','integer'}, ...
        'rmmissing','MinNumOutliers',minnmidx + 1);
    minnm = coder.internal.indexInt(varargin{minnmidx});
else
    minnm = ONE;
end

if DATAVARS_SUPPLIED
    dataVars = varargin{dataVarsIdx};
elseif ISTABLEORTIMETABLE
    dataVars = coder.internal.indexInt(1:width(a));
else
    dataVars = [];
end

if ISTABLEORTIMETABLE
    missLocTmp = coder.internal.applyToTableVariables(a, ...
        @(x) checkDimsAndCollapse(ismissing(x)),dataVars,false,true, ...
        true, '', 'MATLAB:rmmissing:DataVariablesTableSubscript');
    if isempty(missLocTmp) && height(a)~=0
        missLoc = false(height(a),1);
    else
        missLoc = missLocTmp;
    end
    tf = sum(missLoc,2) >= minnm; % Determine which rows to remove.
    b = a(~tf,:);
else
    missLoc = ismissing(a);
    
    if coder.const(dim == 1)
        tf = sum(missLoc,2) >= minnm; % Determine which rows to remove.
        if iscell(a)
            b = rmFromCell(a,tf,ONE);
        else
            b = a(~tf,:);
        end
    else
        tf = sum(missLoc,1) >= minnm; % Determine which columns to remove.
        if iscell(a)
            b = rmFromCell(a,tf,TWO);
        else
            b = a(:,~tf);
        end
    end
end

function out = rmFromCell(in,tf,dim)
coder.internal.prefer_const(dim);
ONE = coder.internal.indexInt(1);
sizeIn = coder.internal.indexInt(size(in));

sizeInDim = coder.internal.indexInt(0);
for ii = ONE:numel(tf)
    if ~tf(ii)
        sizeInDim = sizeInDim + ONE;
    end
end

if dim == 1
    outSize = [sizeInDim, sizeIn(2)];
else
    outSize = [sizeIn(1), sizeInDim];
end

out = cell(outSize);
if dim == 1
    tmp = coder.internal.indexInt(1:sizeIn(1));
    rowToKeep = tmp(~tf);
    columnToKeep = ONE:sizeIn(2);
else
    rowToKeep = ONE:sizeIn(1);
    tmp = coder.internal.indexInt(1:sizeIn(2));
    columnToKeep = tmp(~tf);
end

for jj = ONE:outSize(2)
    for ii = ONE:outSize(1)
        out{ii,jj} = in{rowToKeep(ii),columnToKeep(jj)};
    end
end
coder.assertDefined(out);

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

function out = checkDimsAndCollapse(in)
coder.inline('always');
% Error out if an ND array is given inside a table
coder.internal.assert(ndims(in)<=2,'MATLAB:rmmissing:NDArrays')
out = collapseIntoLogicalColumn(in);