function saveProfile( obj , varargin)
%saveProfile Save modified properties to the current profile
%   saveProfile(cluster) saves the modified properties on the cluster
%   object to the profile specified by the cluster's Profile property, and
%   sets the Modified property to false.  If the cluster's Profile property
%   is empty, then an error is thrown.
%
%   saveProfile(cluster, 'Description', 'text') sets the 
%   description of the saved cluster profile to the value in 'text'.
%
%   Example:
%   % Create a cluster using the 'local' profile.
%   myCluster = parcluster('local');
%
%   % Change the NumWorkers property on myCluster.  
%   % The Modified property will then be true
%   myCluster.NumWorkers = 3;
%
%   % Save myCluster to back to the 'local' profile.  
%   saveProfile(myCluster);
%
%   See also parallel.cluster.Modified, parallel.cluster.saveAsProfile.

% Copyright 2011-2017 The MathWorks, Inc.

import parallel.Cluster

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

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

schedulerName = obj.hGetSchedulerComponentName();
if isempty( obj.ProfileObject )
    schedulerType = parallel.internal.types.SchedulerType.fromName( schedulerName );
    if schedulerType.IsMultipleProfileAllowed
        error(message('parallel:cluster:SaveProfileNoProfileSetUseSaveAs'));
    else
        error(message('parallel:cluster:SaveProfileNoProfileSet'));
    end
end

% NB Always save the profile, even if Modified is false because
% the user may have changed the profile.
cleanupOnErrorFcn = [];
try
    % Set name value pair properties for new profile
    obj.ProfileObject.set(varargin{:});
    [schedComp, cleanupOnErrorFcn] = iGetOrCreateSchedCompForProfile( obj.Type, schedulerName, obj.ProfileObject );
    obj.savePropsToSchedulerComponent( schedComp );
catch err
    if ~isempty( cleanupOnErrorFcn )
        cleanupOnErrorFcn();
    end
    ex = MException(message('parallel:cluster:FailedToSaveProfile', obj.ProfileObject.Name));
    ex = ex.addCause( err );
    throw( ex );
end

obj.setClusterToUnmodified();
end

%---------------------------------------------------------
function [schedComp, cleanupOnErrorFcn] = iGetOrCreateSchedCompForProfile( clusterType, schedulerName, profileObj )
% If the profile's scheduler component is undefined or does not exist,
% then create one of the correct type.
% If the profile has a scheduler component of the wrong type, then error.
import parallel.Cluster

cleanupOnErrorFcn  = [];
profileName = profileObj.Name;

needToCreate = false;
try
    schedComp = profileObj.getSchedulerComponent();
    if ~strcmp( schedComp.Type, schedulerName )
        error(message('parallel:cluster:SaveProfileIncorrectSchedulerComponent', clusterType, profileName, schedComp.Type, profileName));
    end
catch err
    if any( strcmp( err.identifier, ...
            {'parallel:settings:ProfileSchedulerComponentNotDefined', ...
            'parallel:settings:ProfileCannotFindSchedulerComponent'} ) )
        warning(message('parallel:cluster:SaveProfileNoSchedulerComponent', profileName, clusterType, profileName));
        needToCreate = true;
    else
        rethrow( err );
    end
end

if ~needToCreate
    return;
end

schedComp = Cluster.createSchedulerComponent( schedulerName, profileName );
oldSchedCompName = profileObj.SchedulerComponent;
try
    profileObj.SchedulerComponent = schedComp.Name;
    cleanupOnErrorFcn = @nDeleteSchedCompAndRevertProfile;
catch err
    schedComp.delete();
    rethrow( err );
end

    function nDeleteSchedCompAndRevertProfile()
        profileObj.SchedulerComponent = oldSchedCompName;
        schedComp.delete();
    end
end
