function  [residinfo, residh] = bfitplotresids(figH,axesH,datahandle,fitsshowing,subploton,plottype)
%  BFITPLOTRESIDS Plot residuals on figure.

%   Copyright 1984-2014 The MathWorks, Inc.
%     

% constants
barplot = 0;
scatterplot = 1;
lineplot = 2;
infarray = Inf(1,12);
legendLocation = 'northeast';
legendPosition = [];

residinfo = getappdata(double(datahandle),'Basic_Fit_Resid_Info');
residfigure = bfitfindresidfigure(figH, residinfo.figuretag);

% default values in case new fig or axes
axesunits = 'normalized';

% delete old residual axes if it is there
legendH = [];
residaxespos = [];
if ~isempty(residinfo.axes) && ishghandle(residinfo.axes)
    % get old values before delete
    axesunits = get(residinfo.axes,'units');
    legendH = legend('-find',residinfo.axes);
    % keep original legend position if possible
    if ~isempty(legendH)
        legendLocation = get(legendH, 'Location');
        if strcmpi(legendLocation, 'None')
            legendPosition = hgconvertunits(figH, get(legendH,'Position'), get(legendH, 'units'), 'points', get(legendH, 'parent'));
        end
    end
    % Should reset ResidTxt handle since we are deleting it. The Resid_Handles are reset below.
    setappdata(double(datahandle),'Basic_Fit_ResidTxt_Handle', []); % norm of residuals txt
    residaxespos = get(residinfo.axes, 'position'); 
    delete(residinfo.axes);
end

% Create figure/axes and add listeners
if ~subploton % residuals in separate figure
    % Unless we already have a separate figure for resids, get one:
    if isempty(residfigure) || isequal(residfigure, figH)
        % creating a visible figure can cause events to dispatch, make sure they
        % only dispatch after we are ready - start with visible off
        residfigure = figure('DeleteFcn',{@deletefig, figH}, ...
            'Visible','off');
        residinfo.figuretag = datenum(clock);
        % update the appdata so any events generated by showing the figure
        % will not render here and create a new figure
        setappdata(double(datahandle),'Basic_Fit_Resid_Info',residinfo)

        % create tag property on new figure
        bfitAddProp(residfigure, 'Basic_Fit_Fig_Tag', 'on');
        set(handle(residfigure), 'Basic_Fit_Fig_Tag', residinfo.figuretag);

        set(residfigure,'units','pixels');
        residpos = get(residfigure,'position');
        if isappdata(figH, 'Basic_Fit_Resid_Figure_Position')
            set(residfigure,'position', getappdata(figH, 'Basic_Fit_Resid_Figure_Position'));
        elseif isequal(residpos, get(figH,'position'))
            set(residfigure,'position', residpos + [50 -50 0 0]);
        end
        % now show the figure
        figure(residfigure);
    end

    if isempty(bfitFindProp(residfigure,'Basic_Fit_Resid_Figure'))
        bfitAddProp(residfigure, 'Basic_Fit_Resid_Figure');
    end
    set(handle(residfigure),'Basic_Fit_Resid_Figure',1);
    % resize the axesH axes to the original position
    axesHpositionProp = getappdata(double(datahandle),'Basic_Fit_Fits_Axes_Position_Prop');
    axesHposition = getappdata(double(datahandle),'Basic_Fit_Fits_Axes_Position');
    set(axesH,axesHpositionProp,axesHposition);
    residinfo.axes = axes('parent', residfigure); % add a new axes, we already deleted any that existed
    updateResidualAxesXLim(residinfo.axes, axesH);
else % subplot
    if ~isempty(residfigure) && ~isequal(residfigure, figH) % were on separate plots
        delete(residfigure);
        residaxespos = [];
    end
    residfigure = figH;
    residinfo.figuretag = get(handle(figH),'Basic_Fit_Fig_Tag');
    if isempty(residaxespos)
        pos = get(axesH, 'outerposition');
        height = pos(4)/2;
        y = pos(2);
        pos(4) = height;
        pos(2) = y + height ;
        set(axesH,'outerposition', pos);
        pos(2) = y;
        residinfo.axes = axes('outerposition', pos, 'parent', figH);
    else
        residinfo.axes = axes('position', residaxespos, 'parent', figH);
    end
    updateResidualAxesXLim(residinfo.axes, axesH);
end

% Calling drawnow here so that the childContainer will be updated
% properly before listeners are added.
drawnow('expose');

% Code generation for resid line is handled in bfitMCodeConstructor.m
% Have code generation ignore the resid axes here
b = hggetbehavior(residinfo.axes,'MCodeGeneration');
set(b, 'MCodeIgnoreHandleFcn', 'true');

%label the axes a resid axes
if isempty(bfitFindProp(residinfo.axes,'Basic_Fit_Resid_Axes'))
    bfitAddProp(residinfo.axes, 'Basic_Fit_Resid_Axes');
end
set(handle(residinfo.axes),'Basic_Fit_Resid_Axes',1);

% Set resid axes box to be on/off according to data axes
if isequal(get(axesH,'box'),'on')
    set(residinfo.axes,'box','on');
else
    set(residinfo.axes,'box','off');
end
bfitlisten(residfigure) % add listeners to the figure or new axes
bfitlistenoff(figH) % turn off listeners
if ~isequal(figH,residfigure) && ishghandle(residfigure)
    bfitlistenoff(residfigure)
end

% save the figure tag of the data/figs figure in appdata of new resid figure
setappdata(residfigure,'Basic_Fit_Data_Figure_Tag',get(handle(figH),'Basic_Fit_Fig_Tag'));
set(residinfo.axes,'Tag','residuals');
resids = getappdata(double(datahandle),'Basic_Fit_Resids');
xdata = double(get(datahandle,'xdata'));

set(residfigure,'nextplot','add');
set(residinfo.axes,'nextplot','add');
set(residinfo.axes,'units','normalized');

% (re-)initialize to all Inf's
residh = infarray;

% Put residuals in a matrix to plot
% fitsshowing is the indices of the fits to plot
n = length(fitsshowing);
if n > 0
    M = zeros(length(xdata),n);
    %sort resids so lines will look nice
    [sortedxdata, xind] = sort(xdata);
    for i = 1:n
        r = resids{fitsshowing(i)};
        M(:,i) = r(xind);
    end
    nanmask = any(isnan(M), 2);
    M(nanmask, :) = [];
    sortedxdata(nanmask) = [];
    sortedxdata = sortedxdata(:);  % force column to match M's orientation
    switch plottype
        case barplot
            notdistinct = find(~diff(sortedxdata), 1);
            if ~isempty(notdistinct)
                warnmsg = getString(message('MATLAB:graph2d:bfit:WarndlgCannotPlotWithRepeatedXValue'));
                warndlg(warnmsg,getString(message('MATLAB:graph2d:bfit:TitleBasicFitting')));
                return;
            end
            plotresults = bar(residinfo.axes, sortedxdata,M);
            residh(fitsshowing) = plotresults;
            colortype = 'facecolor';
        case scatterplot
            plotresults = plot(sortedxdata,M,'.','parent',residinfo.axes);
            residh(fitsshowing) = plotresults;
            colortype = 'color';
        case lineplot % lineplot
            plotresults = plot(sortedxdata,M,'parent',residinfo.axes);
            residh(fitsshowing) = plotresults;
            colortype = 'color';
        otherwise
            error(message('MATLAB:bfitplotresids:UnknownPlotType'));
    end
    
    for i = 1:length(plotresults)
        % code generation for resid line
        b = hggetbehavior(plotresults(i),'MCodeGeneration');
        set(b, 'MCodeIgnoreHandleFcn', 'true');
        
        % Basic fitting lines should not show in the Property Editor because
        % the user could change the series type and get into an invalid state
        b = hggetbehavior(plotresults(i),'PlotEdit');
        set(b, 'EnableSelect', false);
    end

    for i = 1:n
        name = createresidname(fitsshowing(i)-1);
        % the following is the same as in bfitplotfit so color coincides
        color_order = get(axesH,'colororder');
        % minus one to fitsshowing(i) so fit type is correct
        colorindex = mod(fitsshowing(i)-1,size(color_order,1)) + 1;
        color = color_order(colorindex,:);
        set(residh(fitsshowing(i)),colortype,color,'Tag',name, ...
            'DisplayName', bfitgetdisplayname(fitsshowing(i)-1));
        if (plottype == barplot)
            % set edges to be the same color as bars
            set(residh(fitsshowing(i)),'edgecolor',color);
        end
        fitappdata.type = 'residual';
        fitappdata.index = fitsshowing(i);
        setappdata(double(residh(fitsshowing(i))),'bfit',fitappdata)
        setappdata(double(residh(fitsshowing(i))), 'Basic_Fit_Copy_Flag', 1);
    end
end

% link the resid axis to the plot axis
ax(1) = axesH;
ax(2) = residinfo.axes;
linkaxes(ax, 'x');

% Make ylims centered around 0, and
% if ylims are small, adjust ylim
ylims = get(residinfo.axes,'ylim');
maxy = max(abs(ylims));
if 10*maxy < 1
    set(residinfo.axes,'ylim',[-maxy,maxy].*10);
else
    set(residinfo.axes,'ylim',[-maxy,maxy]);
end

set(get(residinfo.axes,'title'),'string',getString(message('MATLAB:graph2d:bfit:TitleResiduals')))

% Legend only if on separate figure && user has not deleted the legend
% from the separate resid figure.

if ~subploton && ~isempty(fitsshowing)
    % We want to set a legend if this is the first time the resids have
    % been requested to be plotted in a separate figure OR it is not the
    % first time but a legend exists in the separate figure. The appdata
    % below is a flag to indicate "first time ness".
    updateLegend = true;
    if ~isappdata(residfigure, 'Basic_Fit_Data_Stats_first_time_legend_flag')
        setappdata(residfigure, 'Basic_Fit_Data_Stats_first_time_legend_flag', false);
    else
        if isempty(legendH)
            updateLegend = false;
        end
    end

    if updateLegend
        names = {getString(message('MATLAB:graph2d:bfit:DisplayNameSpline')),...
            getString(message('MATLAB:graph2d:bfit:DisplayNameShapePreserving')),...
            getString(message('MATLAB:graph2d:bfit:DisplayNameLinear')),...
            getString(message('MATLAB:graph2d:bfit:DisplayNameQuadratic')),...
            getString(message('MATLAB:graph2d:bfit:DisplayNameCubic')),...
            getString(message('MATLAB:graph2d:bfit:DisplayNameNthDegree','4')),...
            getString(message('MATLAB:graph2d:bfit:DisplayNameNthDegree','5')),...
            getString(message('MATLAB:graph2d:bfit:DisplayNameNthDegree','6')),...
            getString(message('MATLAB:graph2d:bfit:DisplayNameNthDegree','7')),...
            getString(message('MATLAB:graph2d:bfit:DisplayNameNthDegree','8')),...
            getString(message('MATLAB:graph2d:bfit:DisplayNameNthDegree','9')),...
            getString(message('MATLAB:graph2d:bfit:DisplayNameNthDegree','10'))};
        bfitlistenoff(residfigure);
        if strcmpi(legendLocation, 'None')
            legend(residinfo.axes,names(:,fitsshowing), ...
                'Units', 'Points', 'Position', legendPosition);
        else
            legend(residinfo.axes,names(:,fitsshowing), 'Location', legendLocation);
        end
        bfitlistenon(residfigure);
    end
    % if there are no fits, an existing legend will go away, but we want it 
    % come back when fits are plotted again, we need to remove the 
    % "first time" flag.
elseif ~subploton && isempty(fitsshowing) && ~isempty(legendH) && ...
        isappdata(residfigure, 'Basic_Fit_Data_Stats_first_time_legend_flag')
    rmappdata(residfigure, 'Basic_Fit_Data_Stats_first_time_legend_flag')
end

% reset units
set(residinfo.axes,'units',axesunits);

if ~subploton
    bfitlisten(residfigure);
end

% Make axes with data the current axes
if subploton
    set(figH,'currentaxes',axesH);
end

% If equations are showing, reset them (turn then off, then on) because the 
% changes in axes cause by the residuals are likely to have them 
% show up in a funny place.
guistate = getappdata(double(datahandle),'Basic_Fit_Gui_State');
if guistate.equations
    bfitcheckshowequations(false, datahandle, guistate.digits);
    bfitcheckshowequations(true, datahandle, guistate.digits);
end

% if there is an error dialog make sure it stays on top.
dlgh = getappdata(double(datahandle),'Basic_Fit_Dialogbox_Handle');
if ishghandle(dlgh) % if error or warning appeared, make sure it is on top
    figure(dlgh);
end

%----------------------------------
function name = createresidname(fit)
% CREATERESIDNAME  Create tag name for residual line.

switch fit
    case 0
        name = sprintf('residual: spline');
    case 1
        name = sprintf('residual: shape-preserving');
    case 2
        name = sprintf('residual: linear');
    case 3
        name = sprintf('residual: quadratic');
    case 4
        name = sprintf('residual: cubic');
    otherwise
        name = sprintf('residual: %sth degree',num2str(fit-1));
end

function updateResidualAxesXLim(residualAxes, axesH)
    % Removing all the fits when residuals are plotted can corrupt the limits
    % on the axes containing the data. g2146360
    if strcmp(residualAxes.XLimMode,'auto')
        residualAxes.XLim = axesH.XLim;
    end

%----------------------------------
function deletefig(src, ~,figH)
% DELETEFIG Save position when resid figure is deleted.
if ~ishghandle(figH)
    return
end
try
    setappdata(figH, 'Basic_Fit_Resid_Figure_Position', get(src, 'position'));
catch ignore %#ok<NASGU>
end



