% ProjectComponent - Define parameters and properties for creating jobs and tasks
 
%   Copyright 2011-2017 The MathWorks, Inc.

% TODO:doc
% NB This class is hidden for now as the Profiles API is not yet documented
classdef (Hidden) ProjectComponent < parallel.internal.settings.NamedNode
    properties (PCTGetSet, Transient, PCTConstraint = 'cellstr')
        AdditionalPaths
        AttachedFiles
        EnvironmentVariables
    end
    
    properties (PCTGetSet, Transient, PCTConstraint = 'logicalscalar')
        AutoAddClientPath
        AutoAttachFiles
        CaptureDiary
    end

    properties (PCTGetSet, Transient, PCTConstraint = 'callback')
        JobFinishedFcn
        JobQueuedFcn
        JobRunningFcn
        TaskFinishedFcn
        TaskRunningFcn
    end

    properties (PCTGetSet, Transient, PCTConstraint = 'positiveintscalar')
        JobTimeout
        TaskTimeout
    end
    
    properties (PCTGetSet, Transient, PCTConstraint = 'workerlimits')
        NumWorkersRange
    end

    
    properties (Constant, GetAccess = private)
        % NB Remember to add new properties to the relevant sections.
        
        % Properties that apply to jobs/tasks across all schedulers
        CommonJobProperties = {'AdditionalPaths', 'AutoAddClientPath', 'AutoAttachFiles', 'AttachedFiles', 'EnvironmentVariables', 'NumWorkersRange'};
        CommonTaskProperties = {'CaptureDiary'};

        % MJS specific properties
        MJSJobProperties = {'JobFinishedFcn', ...
            'JobQueuedFcn', 'JobRunningFcn', 'JobTimeout'};
        MJSTaskProperties = {'TaskFinishedFcn', 'TaskRunningFcn', 'TaskTimeout'};
    end
    
    methods (Hidden, Static) % Implementation of NamedNode Hidden, Static methods
        function obj = hBuildFromSettings(name, settingsNode, parentSettingsNode) 
            obj = parallel.settings.ProjectComponent(name, settingsNode, parentSettingsNode);
        end
    end

    methods (Access = private) % Constructor
        function obj = ProjectComponent(name, settingsNode, parentSettingsNode, varargin)
            obj@parallel.internal.settings.NamedNode(name, settingsNode, parentSettingsNode, varargin{:});
        end
    end
    
    methods (Hidden)
        function v = hGetProperty(obj, props, varargin)
            v = hGetProperty@parallel.internal.settings.NamedNode(obj, props, varargin{:});
            % Columnify AdditionalPaths and AttachedFiles
            if iscell( props )
                v = cellfun( @iColumnifyIfNecessary, props, v, 'UniformOutput', false );
            else
                v = iColumnifyIfNecessary( props, v );
            end
        end
        
        function [p, v] = hGetJobPVPairs(obj, clusterType)
        % [p, v] = hGetJobPVPairs(obj, clusterType) - Get the PV Pairs
        % that apply to jobs of the specified cluster type.
            import parallel.settings.ProjectComponent
            specificProps = {};
            specificVals  = {};
            if any( strcmp(clusterType, {'MJS', 'MJSComputeCloud', 'Cloud'} ))
                specificProps = ProjectComponent.MJSJobProperties;
                [specificProps, specificVals] = obj.getCollapsedPropertiesNoDefaultValues(specificProps);
                % The MJS job properties don't have the 'Job' prefix on the
                % properties
                specificProps = regexprep(specificProps, '^Job',  '');
            end

            commonProps = ProjectComponent.CommonJobProperties;
            [commonProps, commonVals] = obj.getCollapsedPropertiesNoDefaultValues(commonProps);
            
            p = [commonProps, specificProps];
            v = [commonVals,  specificVals];
        end
        
        function [p, v] = hGetTaskPVPairs(obj, clusterType)
        % [p, v] = hGetJobPVPairs(obj, clusterType) - Get the PV Pairs
        % that apply to tasks of the specified cluster type.
            import parallel.settings.ProjectComponent
            specificProps = {};
            specificVals  = {};
            if any( strcmp(clusterType, {'MJS','MJSComputeCloud', 'Cloud'} ))
                specificProps = ProjectComponent.MJSTaskProperties;
                [specificProps, specificVals] = obj.getCollapsedPropertiesNoDefaultValues(specificProps);
                % The MJS task properties don't have the 'task' prefix on the
                % properties
                specificProps = regexprep(specificProps, '^Task',  '');
            end

            commonProps = ProjectComponent.CommonTaskProperties;
            [commonProps, commonVals] = obj.getCollapsedPropertiesNoDefaultValues(commonProps);
            
            p = [commonProps, specificProps];
            v = [commonVals,  specificVals];
        end
    end
    
    methods (Access = protected) % overrides of NamedNode Abstract methods
        function errorIfInvalidName(~, newName)
            p = parallel.Settings;
            if p.hProjectComponentExists(newName)
                error(message('parallel:settings:ProjectComponentNameAlreadyExists', ...
                    newName));
            end
        end
    end
end
%#ok<*ATUNK> custom attributes

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Convert AdditionalPaths and AttachedFiles to column vectors if necessary
function v = iColumnifyIfNecessary( prop, v )
    if any( strcmp( { 'AdditionalPaths', 'AttachedFiles' }, prop ) ) && ~isempty( v )
        v = reshape( v, numel( v ), 1 );
    end
end
