function saveAsProfile(obj, profileName, varargin)
%saveAsProfile Save modified properties to a profile
%   saveAsProfile(cluster, profileName) saves the modified properties on
%   the cluster object to the profile specified by profileName, and sets
%   this cluster's Profile property to profileName and the Modified
%   property to false.  If the cluster's Profile property is not empty,
%   then the values in the original profile are also copied to the new
%   profile.
%
%   If profileName is the same as the cluster's existing Profile property,
%   then this is the same as calling saveProfile(cluster).  If profileName
%   is different from the cluster's existing Profile property, then there
%   must not already be a profile with the same name as profileName.
%
%   saveAsProfile(cluster, profileName, 'Description', 'text') sets the 
%   description of the saved cluster profile to the value in 'text'.
%
%   Examples:
%   % Create a cluster using the 'local' profile.
%   myCluster = parcluster('local');
%   % Save myCluster to a new profile with name 'localCopy'.
%   % This will change the Profile property on myCluster to
%   % 'localCopy'.
%   saveAsProfile(myCluster, 'localCopy');
%   
%   % Change the NumWorkers property on myCluster.  
%   % The Modified property will then be true
%   myCluster.NumWorkers = 3;
%   % Save myCluster to another new profile with name 'local3Workers'.
%   saveAsProfile(myCluster, 'local3Workers');
%
%   See also parallel.cluster.Modified, parallel.cluster.saveProfile.

% Copyright 2011-2017 The MathWorks, Inc.

% Convert any string inputs to character vectors
[profileName, varargin{:}] = convertStringsToChars(profileName, varargin{:});

validateattributes( obj, {'parallel.Cluster'}, {'scalar'}, 'saveAsProfile', 'cluster', 1);
validateattributes( profileName, {'char'}, {'row'}, mfilename, 'profileName' );
parser = inputParser();
parser.addParameter('Description', @ischar);
parser.parse(varargin{:});

schedulerName = obj.hGetSchedulerComponentName();
schedulerType = parallel.internal.types.SchedulerType.fromName( schedulerName );
if ~schedulerType.IsMultipleProfileAllowed
    error(message('parallel:cluster:SaveAsProfileNotAllowed', obj.Type));
end

% You are allowed to do c.saveAsProfile( obj.Profile ) (this is just saveProfile) 
% but not c.saveAsProfile('anotherExistingName')
if strcmp( profileName, obj.Profile )
    try
        obj.saveProfile(varargin{:});
        return;
    catch err
        throw( err );
    end
end

p = parallel.Settings;
if p.hProfileExists( profileName )
    allNames = p.hGetAllProfileNames();
    error( message( 'parallel:cluster:SaveAsProfileAlreadyExists', ...
        profileName, profileName, profileName, ...
        strjoin( allNames, ', ' ) ) );
end

cleanupOnErrorFcn = [];
try
    [newProfile, schedComp, cleanupOnErrorFcn] = iCreateProfileAndCopyProject( schedulerName, obj.ProfileObject, profileName );
    obj.savePropsToSchedulerComponent( schedComp );
catch err
    if ~isempty( cleanupOnErrorFcn )
        cleanupOnErrorFcn();
    end

    ex = MException(message('parallel:cluster:FailedToSaveProfile', profileName));
    ex = ex.addCause( err );
    throw( ex );
end

% Set name value pair properties for new profile
newProfile.set(varargin{:});
% Set the current profile to be this new name
obj.setProfileObjectAndListeners( newProfile );
obj.setClusterToUnmodified();

end
%----------------------------------------------------------
function [newProf, schedComp, cleanupOnErrorFcn] = iCreateProfileAndCopyProject( schedulerName, origProf, newProfName )
import parallel.Cluster
[newProf, schedComp, createProfCleanup] = Cluster.createProfile( schedulerName, newProfName );

% Copy the project component from the original profile, if there is one.
newPcName = '';
cleanupOnErrorFcn = @nCleanupOnError;

if isempty( origProf )
    return;
end

try
    origPC = origProf.getProjectComponent();
catch err
    if strcmp( err.identifier, 'parallel:settings:ProfileCannotFindProjectComponent' )
        warning(message('parallel:cluster:SaveNonExistentProjectComponent', origProf.Name, newProfName));
        return;
    else
        nCleanupOnError();
        rethrow( err );
    end
end

if isempty( origPC )
    return;
end

% Really copy the project component
proposedName = sprintf( '%sProjectComponent', newProfName );
p = parallel.Settings;
newPcName = p.hGetUnusedProjectComponentName( proposedName );
try
    origPC.saveAs( newPcName );
    % saveAs just pushes the info into the settings.  Force the 
    % project component object to be created by trying to find it.
    p.findProjectComponent( 'Name', newPcName );
    newProf.ProjectComponent = newPcName;
catch err
    nCleanupOnError();
    rethrow( err );
end

    function nCleanupOnError()
        createProfCleanup();
        if ~isempty( newPcName )
            pc = p.findProjectComponent( 'Name', newPcName );
            if ~isempty( pc )
                pc.delete();
            end
        end
    end
end