function A = subsasgn(A, S, B)
%SUBSASGN Subscripted assignment for codistributed array
%   A(I) = B
%   A(I,J) = B for 2-D codistributed arrays
%   A(I1,I2,I3,...,IN) = B for N-D codistributed arrays
%   
%   A{...} indexing is not supported for codistributed cell arrays.
%   A.field indexing is not supported for codistributed arrays of structs.
%   
%   To expand an N-D codistributed array A to higher dimensions via
%   A(I1,I2,...IN,IN+1) = B, B must be a scalar.
%   
%   A(I) = B cannot be used to expand the size of A.
%   
%   Example:
%   spmd
%       N = 1000;
%       D = codistributed.eye(N);
%       D(1,N) = pi
%   end
%   
%   See also SUBSASGN, CODISTRIBUTED, CODISTRIBUTED/EYE.


%   Copyright 2006-2020 The MathWorks, Inc.

% SUBSASGN always needs an output
if nargout==0
    error(message('MATLAB:subsasgnMustHaveOutput'));
end

% Make sure S is a valid substruct
matlab.bigdata.internal.util.checkValidSubstruct(S);

% Convert 'missing' to the underlying type (if possible - this might error)
if isa(B, 'missing') && ~istable(A) && ~istimetable(A)
    aTmpl = distributedutil.Allocator.extractTemplate(A);
    try
        B = cast(B, "like", aTmpl);
    catch
        % Use a subsasgn call to ensure we get the correct error message
        aTmpl(1) = missing; %#ok<NASGU>
    end
end

if builtin('_isEmptySqrBrktLiteral',B)
    % Deleting subsasgn.
    A = hSubsasgnSquareEmpty(A, S);

else
    % Standard or growing. Pick a method depending on datatype of A.
    subsasgnMethods = iGetSubsasgnMethods(A);
    switch S(1).type
        case '()'
            method = subsasgnMethods.Parens;
        case '{}'
            method = subsasgnMethods.Braces;
        case '.'
            method = subsasgnMethods.Dot;
    end
    A = method(A, S, B);
end
end

function subsasgnMethods = iGetSubsasgnMethods(obj)
% TODO: these should probably be in some kind of adaptor class, as per tall arrays,
% so that we can use OO principles instead of switchyards.

% Get all subsasgn method as function handles depending on the underlying
% class
switch underlyingType(obj)
    case {'table', 'timetable'}
        % Table has its own rules for all forms of indexing.
        subsasgnMethods.Parens = @subsasgnTabularParens;
        subsasgnMethods.Braces = @subsasgnTabularBraces;
        subsasgnMethods.Dot    = @subsasgnTabularDot;
    case {'datetime', 'duration', 'calendarDuration'}
        % Allow setting properties using ".", but otherwise standard rules.
        subsasgnMethods.Parens = @subsasgnParens;
        subsasgnMethods.Braces = @subsasgnBraces;
        subsasgnMethods.Dot    = @subsasgnDatetimeDot;
    otherwise
        % All other types follow standard rules.
        subsasgnMethods.Parens = @subsasgnParens;
        subsasgnMethods.Braces = @subsasgnBraces;
        subsasgnMethods.Dot    = @subsasgnDot;
end
end
