function B = subsrefTabularDot(A, aidx)
% subsrefTabularDot performs dot indexing for distributed tables/timetables.
%
% The following syntaxes are allowed:
% B = A.Properties - returns a special struct
% B = A.X          - returns the variable named X as a normal array
% B = A.(n)        - returns the nth variable

% Copyright 2018-2022 The MathWorks, Inc.

template = getTableTemplate(A);
props = template.Properties;
varNames = props.VariableNames;
preserveComplexity = true;
if iscell(aidx.subs)
    sub = aidx.subs{1};
else
    sub = aidx.subs;
end

if istimetable(A)
    rowPropName = "RowTimes";
else
    rowPropName = "RowNames";
end

if strcmp(sub, 'Properties')
    % Copy the properties from the template, but reset the RowTimes for
    % timetables and the RowNames for tables (if any)
    B = props;
    % We must not put a codistributed into the struct, so gather the
    % times/names. If A was distributed in the second dimension this is
    % exactly what we want anyway, but if in the first we must concatenate
    % across local parts, which may blow up memory. In future it would be
    % better to leave this codistributed and rely on the distributed
    % wrapper to piece back together on the client.
    B.(rowPropName) = iGatherRowNamesTimes(A, rowPropName);
    
elseif strcmp(sub, props.DimensionNames{1})
    % Shortcut for getting .Properties.Row[Names/Times]
    B = iExtractRowNamesTimes(A, rowPropName);

elseif strcmp(sub, props.DimensionNames{2})
    % Exactly equivalent to {:,:}
    B = subsrefTabularBraces(A, substruct('{}', {':', ':'}), varNames, preserveComplexity);
    
else
    varIdx = iResolveDotSubscript(sub, varNames);
    
    % Extract the variable from the table
    B = subsrefTabularBraces(A, substruct('{}', {':', varIdx}), varNames, preserveComplexity);
    
end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Resolve a variable subscript into an index
function varIdx = iResolveDotSubscript(subscript, varNames)

% For cases tt.Foo and equivalently tt.('Foo'), we allow the subscript to be
% only: scalar string, char-vector, and numeric integer scalar.

% Handle scalar strings by converting to char.
if isstring(subscript) && isscalar(subscript)
    subscript = char(subscript);
end

if ischar(subscript) || ...
        (isnumeric(subscript) && isscalar(subscript) && round(subscript) == subscript)
    
    if parallel.internal.indexing.isColonLiteral(subscript)
        error(message('MATLAB:table:UnrecognizedVarName', subscript));
    else
        % Resolve numeric integer scalars or char-vectors against
        % known variable names, re-use resolveVarNameSubscript.
        [~,varIdx] = distributedutil.lookupTableVars(subscript, varNames);
        assert(isscalar(varIdx), ...
            'Unexpectedly resolved dot-subscript to multiple variables.');
    end
else
    error(message('MATLAB:table:IllegalVarSubscript'));
end

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Extract rownames/times
function out = iExtractRowNamesTimes(A, propName)

inLP = getLocalPart(A);
inRows = inLP.Properties.(propName);
inCodistr = getCodistributor(A);
distDim = inCodistr.Dimension;

% If distributed in first dimension, we need to simply subsref the local
% parts and form a similarly distributed datetime or duration column
% vector.
if distDim==1
    % Result will either be completely empty on all workers or has the same
    % number of rows as the (time)table. Unfortunately in the empty case we
    % need 0x0 cell rather than a column vector as it would be otherwise.
    numRows = spmdPlus(numel(inRows));
    if numRows == size(A,1)
        outCodistr = codistributor1d(distDim, inCodistr.Partition, [numRows,1]);
        out = codistributed.pDoBuildFromLocalPart(inRows , outCodistr);
    else
        % Build a 0x0 distributed cell
        out = codistributed({}, codistributor1d(distDim));
    end

else
    % For all other distribution schemes the local parts all have the
    % correct time vector unless some (but not all) are empty.
    if all(inCodistr.Partition > 0) || all(inCodistr.Partition == 0)
        % Every worker has the same time vector so build without communicating.
        out = codistributed(inRows);
    else
        % Some workers are empty so send the time vector from the first
        % valid one.
        idx = find(inCodistr.Partition>0, 1, 'first');
        out = codistributed(inRows, idx);
    end
end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Gather time/name vector from a (time)table
function out = iGatherRowNamesTimes(A, rowProp)

% Check for globally empty.
if isempty(A)
    out =  A.Local.Properties.(rowProp);
    return;
end

codistr = getCodistributor(A);
if codistr.Dimension > 1
    % Each local part has all row name/times already.
    out =  A.Local.Properties.(rowProp);
    return;
end

% Deal with names/times spread over workers
rows = A.Local.Properties.(rowProp);
out = spmdCat(rows(:), 1);
% If empty, force to 0x0
if isempty(out)
    out = {};
end
end
