function hh=bubblechart(varargin)
%BUBBLECHART Bubble chart.
%   BUBBLECHART(x,y,sz) displays colored circular markers (bubbles) at the
%   locations specified by the vectors x and y. Specify the size of the
%   bubbles as the vector sz. The vectors x, y, and sz must be the same
%   size. The sz vector specifies the area of each bubble based on a
%   mapping in the current axes to marker size. Use the BUBBLELIM and
%   BUBBLESIZE functions to contol this mapping.
%
%   BUBBLECHART(x,y,sz,c) sets the marker colors using c. When c is a
%   vector the same length as x and y, the values in c are linearly mapped
%   to the colors in the current colormap. When c is a length(x)-by-3
%   matrix, it directly specifies the colors of the markers as RGB triplet
%   values. c can also be a character vector containing a color name, such
%   as 'red'.
%
%   BUBBLECHART(tbl,xvar,yvar,szvar) creates a bubble chart with the
%   variables xvar, yvar, and szvar from table tbl. Multiple bubble charts
%   are created if xvar, yvar, or szvar reference multiple variables. For
%   example, this command creates two bubble charts:
%   bubblechart(tbl, 'var1', {'var2', 'var3'}, 'var4')
%
%   BUBBLECHART(tbl,xvar,yvar,szvar,colorvar) specifies the color of the
%   bubble charts with the table variable colorvar.
%
%   BUBBLECHART(...,Name,Value) sets bubble chart properties using one or
%   more name-value pair arguments. For example:
%   BUBBLECHART(x,y,sz,'MarkerEdgeColor','k') uses a black outline for the
%   markers.
%
%   BUBBLECHART(ax,...) specifies the target axes instead of the current
%   axes.
%
%   bc = BUBBLECHART(...) returns the BubbleChart object. Use bc to set
%   properties on the chart after creating it.
%
%   Example:
%      x = rand(1,50);
%      y = rand(1,50);
%      sz = rand(1,50);
%      bubblechart(x,y,sz)
%
%   See also BUBBLELIM, BUBBLESIZE, BUBBLELEGEND, BUBBLECHART3,
%   POLARBUBBLECHART, SCATTER

%   Copyright 2020-2021 The MathWorks, Inc.

import matlab.graphics.chart.internal.inputparsingutils.peelFirstArgParent
import matlab.graphics.chart.internal.inputparsingutils.splitPositionalFromPV
import matlab.graphics.chart.internal.inputparsingutils.getParent
import matlab.graphics.chart.internal.inputparsingutils.prepareAxes

narginchk(3,inf);

args = varargin;
[parent, args] = peelFirstArgParent(args);

istable = istabular(args{1});
[posargs, pvpairs] = splitPositionalFromPV(args, 3 + istable, true);

hasColor = numel(posargs) == 4 + istable;

if istable
    dataSource = matlab.graphics.data.DataSource(posargs{1});
    dataMap = matlab.graphics.data.DataMap(dataSource);
    dataMap = dataMap.addChannel('X', posargs{2});
    dataMap = dataMap.addChannel('Y', posargs{3});
    dataMap = dataMap.addChannel('Size', posargs{4});
    if hasColor
        dataMap = dataMap.addChannel('Color', posargs{5});
    end

    matlab.graphics.chart.primitive.BubbleChart.validateData(dataMap);
else
    n=cellfun(@numel, posargs(1:3));
    if ~all(n == n(1))
        error(message('MATLAB:bubble:InvalidData', 'X', 'Y'));
    end
    if hasColor
        posargs{4} = validatecdata(posargs{4}, n(1));
        hasColor = hasColor && ~isempty(posargs{4});
    end
    posargs(1:2) = matlab.graphics.chart.internal.getRealData(posargs(1:2), true);
    posargs(3:end) = matlab.graphics.chart.internal.getRealData(posargs(3:end), false);
end


[parent, hasParent] = getParent(parent, pvpairs, 2);
propNames = pvpairs(1:2:end);

% In the case of explicit non-Cartesian axes parents (e.g. polar/geo axes),
% skip validation for 'renamed' property names.
if ~isempty(propNames) && hasParent && ...
        isa(parent, 'matlab.graphics.axis.AbstractAxes') && ...
        ~isa(parent, 'matlab.graphics.axis.Axes')

    propNames = matlab.graphics.chart.primitive.internal.abstractscatter.removeRenamedPropertyNames(...
        propNames, parent.DimensionNames);
end

matlab.graphics.internal.validatePartialPropertyNames(...
    'matlab.graphics.chart.primitive.BubbleChart', propNames);
[parent, ancestorAxes] = prepareAxes(parent, hasParent, false);

if istable
    nObjects = dataMap.NumObjects;
    h = gobjects(1, nObjects);
    for i = 1:nObjects
        sliceStruct = dataMap.slice(i);

        if ~isempty(ancestorAxes)
            x = dataSource.getData(sliceStruct.X);
            y = dataSource.getData(sliceStruct.Y);
            matlab.graphics.internal.configureAxes(ancestorAxes, x{1}, y{1});
        end

        pvcolor={};
        if hasColor
            pvcolor = {'ColorVariable' sliceStruct.Color};
        end

        h(i) = matlab.graphics.chart.primitive.BubbleChart( ...
            'Parent', parent, ...
            'SourceTable', dataSource.Table, ...
            'XVariable', sliceStruct.X, 'YVariable', sliceStruct.Y, ...
            'SizeVariable', sliceStruct.Size, ...
            pvcolor{:}, pvpairs{:});

        h(i).assignSeriesIndex();
    end
else
    if hasColor
        pvpairs = ['CData' posargs{4} pvpairs];
    end

    if ~isempty(ancestorAxes)
        matlab.graphics.internal.configureAxes(ancestorAxes, posargs{1}, posargs{2});
    end

    h = matlab.graphics.chart.primitive.BubbleChart( ...
        'XData', posargs{1}, 'YData', posargs{2}, 'SizeData', posargs{3}, ...
        pvpairs{:}, 'Parent', parent);

    h.assignSeriesIndex();
end

if ~isempty(ancestorAxes)
    if strcmp(ancestorAxes.NextPlot,'replace') || strcmp(ancestorAxes.NextPlot,'replaceall')
        ancestorAxes.Box = 'on';
        view(ancestorAxes,2);
    end
end

if isempty(h)
    h = matlab.graphics.chart.primitive.BubbleChart.empty(1,0);
end

if nargout>0
    hh = h;
end

end


function c=validatecdata(c,ndata)
try
    if ischar(c) || isstring(c)
        % colorspec or hex color
        c = validatecolor(c, 'one');
        ncolors = 1;
    elseif isnumeric(c) && ismatrix(c) && size(c,2)==3
        % RGB color
        c = validatecolor(c, 'multiple');
        ncolors = size(c, 1);
    elseif isvector(c)
        % Colormapped color
        ncolors = numel(c);
    else
        error(message('MATLAB:bubble:InvalidCData'))
    end

    if ncolors ~= ndata && ~isequal(size(c), [1 3])
        error(message('MATLAB:bubble:InvalidCData'))
    end
catch
    throwAsCaller(MException(message('MATLAB:bubble:InvalidCData')));
end

end


