function tt = copyRowTimes(tt, newValue, rows, cols)
% copyRowTimes - copy rowtimes or rownames between a src and destination
% timetable or table, including recursing into variables that are themselves
% tablular. This is needed because (time)table/subsasgn does not correctly
% update row times/names. Optionally a range can be supplied to control which
% rows and columns of the output are updated.

% Copyright 2018-2020 The MathWorks, Inc.

assert(istable(tt) || istimetable(tt), ...
       "Input to copyRowTimes must be tabular");

% Copy times/names for this timetable/table.
rowVar = tt.Properties.DimensionNames{1}; % "RowTimes" for timetables, "Row" for tables.
if ~isequal(newValue.(rowVar), {}) % Ignore tables where RowNames is unset
    if nargin<3 ...
            || parallel.internal.indexing.isColonLiteral(rows) ...
            || isequal(rows, matlab.internal.ColonDescriptor(1, size(tt, 1)))
        % All rows - avoid subsref/asgn
        tt.(rowVar) = newValue.(rowVar);
    else
        % Fill only a few rows. If the result rows are not set yet,
        % initialize. We must use unique values for table RowNames, so we
        % need to pick highly obscure labels. For timetable just fill with
        % a constant.
        if isempty(tt.(rowVar))
            if istimetable(tt)
                tt.(rowVar) = repmat(newValue.(rowVar)(1), height(tt), 1);
            else
                tt.(rowVar) = "__€__" + newValue.(rowVar)(1) + string(1:height(tt))';
            end
        end
        tt.(rowVar)(rows,:) = newValue.(rowVar);
    end
end

% Now recurse into any tabular variables incase they also hold timetables.
if nargin<4 || parallel.internal.indexing.isColonLiteral(cols)
    cols = matlab.internal.ColonDescriptor(1, size(tt, 2));
end
for srcCol=1:numel(cols)
    destCol = cols(srcCol);
    thisVar = tt.(destCol);
    if istable(thisVar) || istimetable(thisVar)
        if nargin>2
            tt.(destCol) = parallel.internal.indexing.copyRowTimes(thisVar, newValue.(srcCol), rows);
        else
            tt.(destCol) = parallel.internal.indexing.copyRowTimes(thisVar, newValue.(srcCol));
        end
    end
end
end
