function joinedStr = strjoin(c, aDelim)
%MATLAB Code Generation Library Function

% Limitations:
%   1. Always returns a string of size 1-by-N. For empty inputs the result is a
%   string of size is 1-by-0 rathern than [].

%   Copyright 2014-2019 The MathWorks, Inc.
%#codegen
%#ok<*EMCA>
narginchk(1, 2);
if nargin == 2
    coder.internal.prefer_const(aDelim);
end

if ~iscellstr(c) && coder.internal.isAmbiguousTypes %#ok<ISCLSTR>
    joinedStr = coder.nullcopy(blanks(coder.internal.ignoreRange(1))); %#ok<NASGU>
    if iscell(c)
        joinedStr = coder.ignoreConst(c{1});
    else
        joinedStr = coder.ignoreConst(c);
    end
    return
end
% Check input arguments.
coder.internal.assert(coder.internal.isReallyCellstr(c), 'MATLAB:strjoin:InvalidCellType');

% Return early when C is empty.
numStrs = coder.internal.indexInt(numel(c));
if coder.internal.isConstTrue(numStrs < 1) && ( nargin < 2 || ischar(aDelim) )
    joinedStr = blanks(0);
elseif nargin < 2
    joinedStr = locStrjoin(numStrs, c, ' ');
elseif ischar(aDelim)
    coder.internal.prefer_const(aDelim);
    joinedStr = locStrjoin(numStrs, c, strescape(aDelim));
else
    coder.internal.assert(iscellstr(aDelim), ...
                          'MATLAB:strjoin:InvalidDelimiterType'); %#ok<ISCLSTR>

    % One more string than delimiters
    numDelim = coder.internal.indexInt(numel(aDelim));
    coder.internal.assert((numStrs > numDelim) && ...
                          (numStrs - numDelim) == ones('like',numStrs), ...
                          'MATLAB:strjoin:WrongNumberOfDelimiterElements');

    joinedStr = locStrjoin(numStrs, c, aDelim);
end

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

function joinedStr = locStrjoin(numStrs, c, theDelim)
coder.inline('always');
coder.internal.prefer_const(theDelim);
CELLDELIM = iscell(theDelim);
assertRowVectorStrings(coder.internal.indexInt(1),c);
if CELLDELIM
    assertRowVectorStrings(coder.internal.indexInt(2),theDelim);
end

joinedStr = coder.nullcopy(blanks(outsize(numStrs, c, theDelim)));
outidx = coder.internal.indexInt(1);

% g1126547: Convert these to subsasgn to eliminate the variable outidx:
% 
%   nStr = coder.itnernal.indexInt(numel(c{k}));
%   joinedStr(outidx:outidx+nStr-1) = c{k};
if numStrs > 0
    for k = 1:numStrs-1
        for j = 1:coder.internal.indexInt(numel(c{k}))
            joinedStr(outidx) = c{k}(j);
            outidx = outidx + 1;
        end
        if CELLDELIM
            for j = 1:coder.internal.indexInt(numel(theDelim{k}))
                joinedStr(outidx) = theDelim{k}(j);
                outidx = outidx + 1;
            end
        else
            for j = 1:coder.internal.indexInt(numel(theDelim))
                joinedStr(outidx) = theDelim(j);
                outidx = outidx + 1;
            end
        end
    end
    for j = 1:coder.internal.indexInt(numel(c{end}))
        joinedStr(outidx) = c{end}(j);
        outidx = outidx + 1;
    end
end

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

function num = outsize(numStrs, c, theDelim)
coder.inline('always');
ZERO = coder.internal.indexInt(0); 
num = ZERO;

% Skip analyzing the delimiter if we only have one string at compile time
if eml_is_const(numStrs) && numStrs == 1
    num = coder.internal.indexInt(numel(c{1}));
else
    coder.internal.prefer_const(numStrs);
    if numStrs > ZERO
        if ischar(theDelim)
            nDelimChars = (numStrs - 1) * coder.internal.indexInt(numel(theDelim));
            num = num + nDelimChars;
            maxnck = coder.internal.indexInt(0);
            for k = 1:numStrs
                nck = coder.internal.indexInt(numel(c{k}));
                num = num + nck;
                if nck > maxnck
                    maxnck = nck;
                end
            end
            assert(num <= nDelimChars + maxnck*numStrs);% <HINT>
        else
            maxnck = coder.internal.indexInt(0);
            maxndk = coder.internal.indexInt(0);
            for k = 1:numStrs - 1
                nck = coder.internal.indexInt(numel(c{k}));
                ndk = coder.internal.indexInt(numel(theDelim{k}));
                num = num + ndk + nck;
                if nck > maxnck
                    maxnck = nck;
                end
                if ndk > maxndk
                    maxndk = ndk;
                end
            end
            nck = coder.internal.indexInt(numel(c{end}));
            num = num + nck;
            if nck > maxnck
                maxnck = nck;
            end
            assert(num <= maxnck*numStrs + maxndk*(numStrs-1)); % <HINT>
        end
    end
end

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

function escapedStr = strescape(str)
coder.inline('always');
coder.internal.prefer_const(str);
k = coder.internal.indexInt(1);
outk = k;
nchars = coder.internal.indexInt(numel(str));

escapedStr = coder.nullcopy(str);

% Can only contain an escape sequence if there is more than character
if nchars > 1
    while k <= nchars
        c = str(k);
        if c == '\' && k < nchars
            % Only copy new character; skip past \c
            k = k + 1;
            c = escapeChar(str(k));
            escapedStr(outk) = c;
        else
            % No \ or trailing \ so copy it
            escapedStr(outk) = c;
        end
        k = k + 1;
        outk = outk + 1;
    end
    escapedStr = escapedStr(1:outk-1);
else
    escapedStr = str;
end

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

function c = escapeChar(c)
switch c
  case '0'  % Null.
    c = char(0);
  case 'a'  % Alarm.
    c = char(7);
  case 'b'  % Backspace.
    c = char(8);
  case 'f'  % Form feed.
    c = char(12);
  case 'n'  % New line.
    c = char(10); %#ok<CHARTEN>
  case 'r'  % Carriage return.
    c = char(13);
  case 't'  % Horizontal tab.
    c = char(9);
  case 'v'  % Vertical tab.
    c = char(11);
  case '\'  % Backslash.
    c = '\';
  otherwise
    coder.internal.warning('MATLAB:strescape:InvalidEscapeSequence', c, c);
end

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

function assertRowVectorStrings(argIdx, c)
coder.inline('always');
if ~isempty(c)
    if ~coder.target('MATLAB') && coder.internal.isHomogeneousCell(c)
        coder.internal.assert(isempty(c{1}) || isrow(c{1}), 'Coder:toolbox:StrjoinRowVectorString', 1, argIdx);
    else
        for k = 1:coder.internal.indexInt(numel(c))
            coder.internal.assert(isempty(c{k}) || isrow(c{k}), 'Coder:toolbox:StrjoinRowVectorString', k, argIdx);
        end
    end
end

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