% MJS - Scheduler Component for MathWorks Job Scheduler
 
%   Copyright 2011-2022 The MathWorks, Inc.

% TODO:doc properties
% NB This class is hidden for now as the Profiles API is not yet documented
classdef (Hidden, Sealed) MJS < parallel.settings.SchedulerComponent

    %---------------------------------------------------
    % Properties stored in Settings
    %---------------------------------------------------
    properties (PCTGetSet, Transient, PCTConstraint = 'string')
        MJSName
        Username
        LicenseNumber
    end
    properties (PCTGetSet, Transient, PCTConstraint = 'hostname')
        Host
    end
    properties ( PCTGetSet, Transient, PCTConstraint = 'string', Hidden )
        Certificate
        ClientCertificate
        ClientPrivateKey
    end    
    properties (PCTGetSet, Transient, PCTConstraint = 'positiveintscalar')
        DefaultTimeout
        MaximumRetries
    end
    properties (PCTGetSet, Transient, PCTConstraint = 'logicalscalar')
        RestartWorker
    end
    
    %---------------------------------------------------
    % Normal class properties
    %---------------------------------------------------
    properties (Constant, Access = private)
        JobProperties = {'DefaultTimeout', 'RestartWorker'};
        TaskProperties = {'DefaultTimeout', 'MaximumRetries'};
    end

    methods (Hidden, Static) % Implementation of NamedNode Hidden, Static methods
        function obj = hBuildFromSettings(name, settingsNode, parentSettingsNode, varargin) 
            obj = parallel.settings.schedulerComponent.MJS(name, settingsNode, parentSettingsNode, varargin{:});
        end
    end

    methods (Access = private) % Constructor
        function obj = MJS(name, node, parentNode, varargin)
            import parallel.internal.types.SchedulerType
            obj@parallel.settings.SchedulerComponent(name, ...
                SchedulerType.MJS, node, parentNode, varargin{:});
        end
    end

    methods (Hidden) % overrides of SchedulerComponent methods
        function [p, v] = hGetClusterPVPairs(obj)
            % all gettable settings props except the JobProperties and TaskProperties are cluster props
            import parallel.settings.schedulerComponent.MJS
            
            gettableProps = obj.hGetPublicSettingsProperties();
            % Remove the Alias key from the list
            gettableProps = setdiff(gettableProps, {parallel.internal.settings.NamedNode.AliasKeyName});
            p = setdiff(gettableProps, [MJS.JobProperties, MJS.TaskProperties]);
            [p, v] = obj.getCollapsedPropertiesNoDefaultValues(p);
            
            % Make sure that "MJSName" becomes just "Name"
            [profileNames, clusterNames] = iGetClusterProfilePropConverter();
            for ii = 1:numel(profileNames)
                p = strrep(p, profileNames{ii}, clusterNames{ii});
            end
        end
        
        function [p, v] = hGetJobPVPairs(obj, resolveWithProps, resolveWithVals)
            import parallel.settings.schedulerComponent.MJS
            import parallel.internal.settings.MJSHelper

            [p, v] = obj.getCollapsedPropertiesNoDefaultValues(MJS.JobProperties);
            p = [p(:); resolveWithProps(:)].';
            v = [v(:); resolveWithVals(:)].';
            [p, v] = MJSHelper.resolveTimeouts(p, v);
        end

        function [p, v] = hGetTaskPVPairs(obj, resolveWithProps, resolveWithVals)
            import parallel.settings.schedulerComponent.MJS
            import parallel.internal.settings.MJSHelper

            [p, v] = obj.getCollapsedPropertiesNoDefaultValues(MJS.TaskProperties);
            p = [p(:); resolveWithProps(:)].';
            v = [v(:); resolveWithVals(:)].';
            [p, v] = MJSHelper.resolveTimeouts(p, v);
        end

        function hSetFromCluster(obj, names, values)
        % hSetFromCluster(obj, names, values) - Set a cluster's names
        % and values on this scheduler component.
            validateattributes(names, {'cell'}, {});
            validateattributes(values, {'cell'}, {});
            
            % Make sure that Name becomes MJSName etc
            names = iReplaceClusterWithProfileNames(names);
            hSetFromCluster@parallel.settings.SchedulerComponent(obj, names, values);
        end
        
        function hSetFromClusterIfNotAlreadySet(obj, names, values)
        % hSetFromClusterIfNotAlreadySet(obj, names, values) - Set a 
        % cluster's names and values on this scheduler component only
        % if those names do not already have the supplied values at
        % the collapsed level
            validateattributes(names, {'cell'}, {});
            validateattributes(values, {'cell'}, {});
            
            % Make sure that Name becomes MJSName etc
            names = iReplaceClusterWithProfileNames(names);
            hSetFromClusterIfNotAlreadySet@parallel.settings.SchedulerComponent(...
                obj, names, values);
        end

    end
    
    methods (Hidden) % overrides of CustomPropDisp
       function [n, f] = hCustomizeDisplay(obj, level)
            n = {'Host', 'MJSName', 'Username', ...
                'DefaultTimeout', 'RestartWorker', 'MaximumRetries', 'LicenseNumber'};
            if nargin < 2
                f = obj.get(n);
            else
                f = cellfun(@(x) obj.get(x, level), n, 'UniformOutput', false);
            end
        end
    end

    methods (Access = protected) % Implementation of SchedulerComponent abstract methods
        function tf = isAlreadyDiscovered(obj, names, values)
            names = iReplaceClusterWithProfileNames(names);
            propsToCompare = {'MJSName', 'Host'};
            objVals = obj.extractValuesFromNamesValues(propsToCompare, names, values);
            
            tf = strcmp(obj.MJSName, objVals{1}) && ...
                obj.isEquivalentHost(objVals{2});
        end
    end
end
%#ok<*ATUNK> custom attributes

%----------------------------------------------
function [profileNames, clusterNames] = iGetClusterProfilePropConverter()
profileNames = {'MJSName'};
clusterNames = {'Name'};
end

%----------------------------------------------
function names = iReplaceClusterWithProfileNames(names)
[profileNames, clusterNames] = iGetClusterProfilePropConverter();
for ii = 1:numel(profileNames)
    names = strrep(names, clusterNames{ii}, profileNames{ii});
end
end
