function out = append(varargin)
%MATLAB Code Generation Library Function

%   Limitations:
%
%   1. Cannot generate code with a mix of heterogeneous cell inputs and var-sized cell inputs
%   2. All character inputs (both direct inputs and ones contained in cell inputs)
%      must be compile-time row vectors

%   Copyright 2023 The MathWorks, Inc.
%#codegen

narginchk(1, inf);

outputTypeCell = validateInputs(varargin{:});
ONE = coder.internal.indexInt(1);
ZERO = coder.internal.indexInt(0);

if outputTypeCell
    nd = coder.const(outputNDims(varargin{:}));
    outputSize = computeOutputSize(nd, varargin{:});
    out = coder.nullcopy(cell(outputSize));

    % If any of the dimension is zero return
    if isempty(out)
        return;
    end

    containsHeterogeneousCell = hasHeterogeneousCell(varargin{:});

    % Loop over output elements
    coder.unroll(containsHeterogeneousCell)
    for oo = ONE:numel(out)
        temp = cell(1, nargin);
        outputIdx = cell(1, nd);
        [outputIdx{:}] = coder.internal.ind2sub(outputSize, oo);
        % Loop over input arguments
        for k = ONE:nargin
            % Input is array only if input is cell. Char vector treated as scalar
            if iscell(varargin{k})
                if isscalar(varargin{k})
                    % if input is a scalar
                    % Append the scalar element to each of the output elements
                    temp{k} = varargin{k}{1};
                else
                    % If input cell array is not a scalar
                    inputKSize = coder.internal.indexInt(size(varargin{k}));
                    % If input is of the same size of the output
                    % Perform direct mapping between input and output for append
                    if isequal(inputKSize, outputSize)
                        % if no expansion use direct mapping
                        % Loop over output index
                        temp{k} = varargin{k}{oo};
                    else
                        inputK = varargin{k};
                        ndK = coder.internal.ndims(inputK);
                        inputIdx = cell(1, ndK);
                        for ii = ONE:ndK
                            if outputIdx{ii} > size(inputK, ii)
                                inputIdx{ii} = ONE;
                            else
                                inputIdx{ii} = outputIdx{ii};
                            end
                        end

                        % append output to input
                        temp{k} = inputK{inputIdx{:}};
                    end
                end
            else
                temp{k} = varargin{k};
            end
        end
        out{oo} = [temp{:}];
    end

else % When all inputs are character vectors, output is a character vector
    % Compute the output size
    outSize = ZERO;
    for k = ONE:nargin
        outSize = outSize + coder.internal.indexInt(size(varargin{k}, 2));
    end
    % Create the output char row vector
    out = coder.nullcopy(char(zeros(1, outSize)));

    % append each of the input to output vector
    startIndex = ZERO;
    for k = ONE:nargin
        sizeK = coder.internal.indexInt(size(varargin{k}, 2));
        out((startIndex+1):startIndex+sizeK) = varargin{k};
        startIndex = startIndex + sizeK;
    end
end

%----------------------------------------
function outputTypeCell = validateInputs(varargin)
% Validate the input and check if any input is a cell
% If any of the input is a cell then the output is of type cell
outputTypeCell = false;
for k = 1:nargin
    if iscell(varargin{k})
        coder.internal.assert(coder.internal.isCellOfCharRow(varargin{k}), ...
            'Coder:toolbox:MustBeCharCellArrayOrString', k);
        outputTypeCell = true;
    else
        coder.internal.assert(checkInputChar(varargin{k}), ...
            'Coder:toolbox:MustBeCharCellArrayOrString', k);
    end
end

%----------------------------------------
function sz = computeOutputSize(nd, varargin)
% Compute the dimension of output considering implicit expansion
sz = ones(1,nd,coder.internal.indexIntClass);
coder.unroll;
for k = 1:(nargin-1)
    if iscell(varargin{k})
        coder.unroll;
        for j = 1:nd
            szkj = size(varargin{k},j);
            if szkj ~= 1
                if sz(j) == 1
                    sz(j) = szkj;
                else
                    % Error if there is dimension mismatch
                    coder.internal.assert(szkj == sz(j), 'MATLAB:dimagree');
                end
            end
        end
    end
end

%----------------------------------------
function nd = outputNDims(varargin)
% Compute the number of dimensions of the output
nd = coder.internal.ndims(varargin{1});
coder.unroll;
for k = 2:nargin
    nd = coder.const(max(nd,coder.internal.ndims(varargin{k})));
end

%----------------------------------------
function charVec = checkInputChar(ipCharVec)
% When object arrays are supported consider using
% toolbox/eml/lib/matlab/strfun/private/isTextStrict.m
% Check if input is a row character vector
charVec = coder.internal.isConstTrue(ischar(ipCharVec) && isrow(ipCharVec));

%----------------------------------------
function out = hasHeterogeneousCell(varargin)
% Function to return true if any cell is heterogeneous
out = false;
for i = 1:nargin
    if iscell(varargin{i})
        if ~coder.internal.isHomogeneousCell(varargin{i})
            out = true;
            return;
        end
    end
end
