function b = addvars(a, varargin)
%ADDVARS Add variables to tall table or timetable.
%   T2 = ADDVARS(T1, VAR1, ..., VARN)
%   T2 = ADDVARS(..., 'Before', LOCATION) 
%   T2 = ADDVARS(..., 'After',  LOCATION)
%   T2 = ADDVARS(..., 'NewVariableNames', NEWNAMES)
%
%   See also TABLE, TALL.

%   Copyright 2018 The MathWorks, Inc.

% Make sure that the first input is a tall table/timetable.
thisFcn = upper(mfilename);
tall.checkIsTall(thisFcn, 1, a);
a = tall.validateType(a, thisFcn, {'table', 'timetable'}, 1);

% Make sure that the new variables to add are tall array/table/timetable
nvNames = {'Before', 'After', 'NewVariableNames'};
numNewVars = numel(varargin);
for ii = 1:numel(varargin)
    in = varargin{ii};
    isStringInput = matlab.internal.datatypes.isScalarText(in);
    if isStringInput && any(startsWith(nvNames, in, 'IgnoreCase', true))
        numNewVars = ii - 1;
        break;
    end
end
for ii = 1:numNewVars
    tall.checkIsTall(thisFcn, 1+ii, varargin{ii});
end

% Use the in-memory version to do input checking
bProto = tall.validateSyntax(@addvars, [{a},varargin], 'DefaultType', 'double');

% Extract tall new variables and optional name-value pairs
newVars = varargin(1:numNewVars);
[varargin(1:numNewVars)] = [];
optArgs = varargin;

% Check if NewVariableNames name-value pair has been provided
newVarNamesProvided = false;
for ii = 1:numel(optArgs)
    in = optArgs{ii};
    isStringInput = matlab.internal.datatypes.isScalarText(in);
    if isStringInput && any(startsWith('NewVariableNames', in, 'IgnoreCase', true))
        newVarNamesProvided = true;
    end
end

% If the variable names were automatically determined, we may need to use
% inputname to rename them.
if ~newVarNamesProvided
    oldVarNames = a.Adaptor.getVariableNames();
    allVarNames = bProto.Properties.VariableNames;
    [newVarNames, newIdx] = setdiff(allVarNames, oldVarNames, "stable");
    for ii=1:numNewVars
        inName = inputname(ii+1);
        if ~isempty(inName)
            newVarNames{ii} = inName;
        end
    end
    % Make sure the new names don't conflict with existing variables names.
    newVarNames = matlab.lang.makeUniqueStrings(newVarNames, oldVarNames, namelengthmax);
    % Update name-value pairs
    optArgs = [optArgs{:}, {'NewVariableNames'}, {newVarNames}];
    % Update bProto to get the adaptor with the updated new variable names.
    bProto.Properties.VariableNames(newIdx) = newVarNames;
end

% Use slicefun to call in-memory addvars
b = slicefun(@(a, varargin) addvars(a, varargin{:}, optArgs{:}), a, newVars{:});

% Update the adaptor for b with the prototype from tall/validateSyntax
adaptorB = matlab.bigdata.internal.adaptors.getAdaptor(bProto);
b.Adaptor = resetTallSize(adaptorB);
end