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(...,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 The MathWorks, Inc.

[args,cax,parent,pvpairs]=parseBubbleInputs(varargin{:});

cDataProp='CData';
if numel(args)<4 || isempty(args{4})
    % no color specified, get it from nextstyle if there is an axes
    % otherwise default to black
    c=[0 0 0];
    if ~isempty(cax)
        [~,c,~] = matlab.graphics.chart.internal.nextstyle(cax,true, false, true);
    end
    cDataProp='CData_I';
else
    c=args{4};
end

% makeNumeric will need an axes to convert non-numeric, creating it here so
% that configureAxes can run before calling makeNumeric.
if isempty(cax) && (~isnumeric(args{1}) || ~isnumeric(args{2}))
    cax=gca;
end

if ~isempty(cax)
    matlab.graphics.internal.configureAxes(cax,args{1},args{2});
    [args{1},args{2}]=matlab.graphics.internal.makeNumeric(cax,args{1},args{2});
end

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

% if there was no axes and no color, make SeriesIndex 1, so that color will
% use the first color in colororder rather than black (default used above)
if isempty(cax) && (numel(args)<4 || isempty(args{4}))
    h.SeriesIndex=1;
else
    h.assignSeriesIndex();
end

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

if nargout>0, hh = h; end

end


function [args,cax,parent,pvpairs]=parseBubbleInputs(varargin)
if nargin<3
    throwAsCaller(MException(message('MATLAB:narginchk:notEnoughInputs')));
end

cp = matlab.graphics.chart.ChartInputParser('ChartClassName', 'matlab.graphics.chart.primitive.BubbleChart');
pvpairs = cp.parseNameValue(varargin{:});  
if isempty(pvpairs)
    pvpairs={};
end
[hasParent, parent] = cp.parseInitialParent;       
args= cp.getRemainingArgs;

if numel(args)<3
    throwAsCaller(MException(message('MATLAB:narginchk:notEnoughInputs')));
elseif numel(args)>4
    throwAsCaller(MException(message('MATLAB:narginchk:tooManyInputs')));
end

n=cellfun(@numel,args(1:3));
if ~all(n==n(1))
    throwAsCaller(MException(message('MATLAB:bubble:InvalidData')));
end


if numel(args)==4 
    try
        args{4}=validatecdata(args{4},n(1));
    catch
        throwAsCaller(MException(message('MATLAB:bubble:InvalidCData')));
    end
end

try
    args(1:2)=matlab.graphics.chart.internal.getRealData(args(1:2),true);
    args(3:end)=matlab.graphics.chart.internal.getRealData(args(3:end),false);
catch me
    throwAsCaller(me)
end


if hasParent
    if ~isempty(parent) 
        if ~isvalid(parent)
            throwAsCaller(MException(message('MATLAB:class:InvalidHandle')));
        end

        if isa(parent, 'matlab.graphics.axis.AbstractAxes')
            cax = newplot(parent);
        else
            cax = ancestor(parent, 'matlab.graphics.axis.AbstractAxes');
        end
    else
        % explicitly parented to empty:
        cax=[];
        parent=[];
    end
else
    % no parent specified, make one
    cax = newplot;
    parent=cax;
end

end

function c=validatecdata(c,ndata)
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
end
