function mx = newmap(varargin)
% NEWMAP Create a new map
%
%    NEWMAP prepares a figure and mapaxes for subsequent graphics commands.
% 
%    NEWMAP(proj) specifies the map projection of the MapAxes object using
%    a projcrs object.
% 
%    NEWMAP(target,___) prepares the object specified by target for
%    plotting instead of the current axes of the current figure. The target
%    object can be any type of axes object or mapaxes parent.
% 
%    mx = NEWMAP(___) returns the MapAxes object. Use mx to modify
%    properties of the axes after you create it.
%    
%    See also cla, hold, mapaxes, newplot

%    Copyright 2022-2023 The MathWorks, Inc.

    import matlab.graphics.chart.internal.inputparsingutils.observeFigureNextPlot

    narginchk(0,2)
    
    proj = [];
    target = [];
    if nargin == 1
        if isa(varargin{1},'projcrs')
            proj = varargin{1};
        elseif isgraphics(varargin{1})
            target = varargin{1};
        elseif isa(varargin{1},'matlab.graphics.Graphics')
            error(message('MATLAB:newplot:InvalidHandle'))
        else
            error(message('map:crs:MustBeProjected'))
        end
    elseif nargin == 2
        proj = varargin{2};
        if ~isa(proj,'projcrs')
            error(message('map:crs:MustBeProjected'))
        end
        target = varargin{1};
        if ~isgraphics(target)
            error(message('MATLAB:newplot:InvalidHandle'))
        end
    end

    mustBeScalarOrEmpty(proj)
    mustBeScalarOrEmpty(target)

    if ~isempty(proj)
        [forward, inverse] = supportedTransformations(proj);
        if ~forward
            error(message('map:graphics:MustHaveForwardProjection'))
        elseif ~inverse
            error(message('map:graphics:MustHaveInverseProjection'))
        end
    end
    
    ax = ancestor(target,'matlab.graphics.axis.AbstractAxes');
    fig = ancestor(target,'figure');
    
    % If no axes or figure was found, create a new figure. Check if the
    % axes is empty to avoid creating a figure if an unparented axes was
    % provided.
    if isempty(fig) && isempty(ax)
        fig = gcf;
    end
    
    if ~isempty(fig)
        % Prepare the figure based on the NextPlot property.
        fig = observeFigureNextPlot(fig, target);
    end
    
    checkAxesNextPlot = true;
    if isempty(ax)
        ax = fig.CurrentAxes;
        if isempty(ax)
            if ~isempty(target) && isgraphics(target) ...
                    && ~isa(target,'matlab.graphics.mixin.MapAxesParentable')
                % If target is not a supported data object, assume target
                % is intended to be the parent of the mapaxes.
                % This behavior is distinct from newplot.
                ax = mapaxes('Parent',target);
            else
                ax = mapaxes('Parent',fig);
            end
            % A new mapaxes was just created, no need to check NextPlot.
            checkAxesNextPlot = false;
        elseif ~isa(ax,'map.graphics.axis.MapAxes')
            if isprop(ax,'NextPlot') && ishold(ax)
                error(message('map:graphics:HoldOnMixing',ax.Type))
            else
                ax = matlab.graphics.internal.swapaxes(ax, @mapaxes);
            end
            % A new mapaxes was just created, no need to check NextPlot.
            checkAxesNextPlot = false;
        end
    elseif ~any(isgraphics(ax))
        error(message('MATLAB:newplot:NoAxesParent'))
    elseif ~isa(ax,'map.graphics.axis.MapAxes')
        % Ensure that the output axes is a mapaxes.
        % This behavior is distinct from newplot.
        if isprop(ax,'NextPlot') && ishold(ax)
            error(message('map:graphics:HoldOnMixing',ax.Type))
        else
            ax = matlab.graphics.internal.swapaxes(ax, @mapaxes);
        end
        % A new mapaxes was just created, no need to check NextPlot.
        checkAxesNextPlot = false;
    end
    
    if checkAxesNextPlot
        ax = prepareAxesNextPlot(ax, target);
    end
    
    if ~isempty(proj)
        set(ax,'ProjectedCRS',proj)
    end
    
    if nargout
        mx = ax;
    end
end


function ax = prepareAxesNextPlot(ax, target)
% Prepare mapaxes using its NextPlot property, optionally preserving
% specific existing descendants.

    % For performance, only call ancestor when needed
    fig = ax.Parent;
    if ~isempty(fig) && ~strcmp(fig.Type,'figure')
        fig = ancestor(fig,'figure');
    end

    switch ax.NextPlot
        case 'replaceall'
            cla(ax, 'reset', target);
        case 'replace'
            % Only 1 dataspace has data, use reset flag
            cla(ax, 'reset', target);
        case 'replacechildren'
            cla(ax, target);
        case 'add'
            % Nothing to be done for NextPlot 'add'
    end

    % Ensure that this function returns a mapaxes.
    if ~any(isgraphics(ax)) && isempty(target)
        if ~any(isgraphics(fig))
            ax = mapaxes;
        else
            ax = mapaxes('Parent',fig);
        end
    end
end
