% ClusterDisplayer - Displayer subclass to display Cluster objects

% Copyright 2011-2018 The MathWorks, Inc.

classdef ( Hidden ) ClusterDisplayer < parallel.internal.display.Displayer
    properties (Constant, GetAccess = private)
        BaseDisplayClass = 'parallel.Cluster';
        VectorDisplayProps = {'Type', 'Profile' 'Host'};
        VectorTableColumns = { ...
            % 'Title', 'Resizeable', 'MinimumWidth'
            '',        true,         length('1') % Index column for the vector display 
            'Type',    false,        length('MJSComputeCloud'); ...
            'Profile', true,         length('Profile'); ...
            'Host',    true,         length('Host'); ...
            }
    end
    
    properties (SetAccess = immutable, GetAccess = protected) % implementation for abstract Displayer properties
        DisplayHelper  
    end
    
    methods
        function obj = ClusterDisplayer(someObj, clusterClass)
            if nargin < 2
                clusterClass = parallel.internal.cluster.ClusterDisplayer.BaseDisplayClass; 
            end
            obj@parallel.internal.display.Displayer(someObj, clusterClass);
            obj.DisplayHelper = parallel.internal.display.DisplayHelper( length('RequiresClientCertificate') );
        end
       
    end
    methods (Access = protected)
        function doSingleDisplay(obj, toDisp)
            % we want links to the specific help for the individual cluster subtype in the 
            % single display, so we don't use the generic class display name
            obj.DisplayHelper.displayMainHeading( '%s %s', obj.formatDocLink(class(toDisp)), getString(message('parallel:cluster:Cluster')) ); 
            
            obj.DisplayHelper.displaySubHeading(getString(message('parallel:cluster:Properties')));
            obj.displaySpecificItems(toDisp);
            
            obj.DisplayHelper.displaySubHeading('%s', char(obj.hGetJobsLink(toDisp, getString(message('parallel:cluster:AssociatedJobs')) )));
            [p, q, r, f] = obj.getNumJobs(toDisp);
            obj.DisplayHelper.displayProperty('Number Pending',  p);
            obj.DisplayHelper.displayProperty('Number Queued',   q);
            obj.DisplayHelper.displayProperty('Number Running',  r);
            obj.DisplayHelper.displayProperty('Number Finished', f);
        end
        function doVectorDisplay(obj, toDisp)
            import parallel.internal.display.Displayer
            import parallel.internal.cluster.ClusterDisplayer
            tableData = cell(numel(toDisp), size(ClusterDisplayer.VectorTableColumns, 1));
            for ii = 1:numel(toDisp)
                if isvalid(toDisp(ii))
                    propsToDisp = Displayer.getDisplayPropsAsStruct(toDisp(ii), ClusterDisplayer.VectorDisplayProps);
                    tableData(ii, :) = {...
                        obj.hGetLink(toDisp(ii), ii), ...
                        propsToDisp.Type, ...
                        propsToDisp.Profile, ...
                        propsToDisp.Host};
                else
                    tableData(ii, :) = {parallel.internal.display.AbstractDisplayer.DeletedString, '', ''};
                end
            end
            % In the vector display we only want to display help for the common base class 
            obj.DisplayHelper.displayDimensionHeading(size(toDisp), obj.formatDocLink(ClusterDisplayer.BaseDisplayClass));
            % The index column should not be added to the table by the
            % DisplayHelper, because we have already added it here in the ClusterDisplayer. 
            obj.DisplayHelper.displayTable(ClusterDisplayer.VectorTableColumns, tableData, obj.DisplayHelper.DoNotDisplayIndexColumn);
        end
        
        % Override displaySpecificItems for clusters only because of required
        % licensing check 
        function displaySpecificItems(obj, toDisp)
            % Gets the specified properties from the toDisp object and
            % displays them.
            if ~isscalar(toDisp)
                error(message('parallel:cluster:DisplaySpecificItemsNotScalar'));
            end
            
            diFactory = parallel.internal.display.DisplayableItemFactory(obj.DisplayHelper);
            [clusterPropertyMap, propNames] = toDisp.hGetDisplayItems(diFactory);
           
            for ii = 1:numel(propNames)
                displayValue = clusterPropertyMap(propNames{ii});
                displayValue.displayInMATLAB(propNames{ii});
                % Only display license number if we require MW licensing
                if strcmp('RequiresOnlineLicensing', propNames{ii})
                    requireLicensingVal = clusterPropertyMap('RequiresOnlineLicensing').getValue();
                    % Note that requireLicensingVal could be empty if we
                    % failed to get the value from the cluster.
                    if islogical(requireLicensingVal) && requireLicensingVal 
                        displayValue = clusterPropertyMap('LicenseNumber');
                        displayValue.displayInMATLAB('LicenseNumber');
                    end
                end
            end
        end        
    end
 	 
     methods ( Access = protected )
        function [numPending, numQueued, numRunning, numFinished] = getNumJobs(~,toDisp)
            [p, q, r, f] = toDisp.findJob();
            
            numPending  = numel(p);
            numQueued   = numel(q);
            numRunning  = numel(r);
            numFinished = numel(f);
        end
        
     end
     
     methods ( Hidden, Sealed )
         function jobsLink = hGetJobsLink(obj, toDisp, displayValue)
             if obj.ShowLinks
                 % Use a MinimalClusterMemento because we only use it to
                 % access the Cluster's jobs.
                 matlabFunction = sprintf('parallel.internal.cluster.ClusterDisplayer.displayJobs(''%s'')', ...
                     serialize(parallel.internal.display.MinimalClusterMemento(toDisp)));
                 jobsLink = parallel.internal.display.HTMLDisplayType(displayValue, matlabFunction);
             else
                 % Getting a link will always return an HTMLDisplayType,
                 % but if we are not building a hyperlink, we do not
                 % include the matlab command. 
                 jobsLink =  parallel.internal.display.HTMLDisplayType(displayValue);
             end
         end
     end
     
     methods ( Hidden, Static )
        % DisplayJobs when the hyperlink is clicked in the ClusterDisplay
        function displayJobs(serializedMemento)
            try
                cluster = createObjectFromMemento(parallel.internal.display.Memento.deserialize(serializedMemento));
                disp(cluster.Jobs)
            catch err
                if feature('hotlinks')
                    helpString = '<a href="matlab: helpPopup parallel.Cluster">help parallel.Cluster</a>';
                else
                    helpString =  'help parallel.Cluster';
                end
                error(message('parallel:cluster:LinkDispFailure', getString(message('parallel:cluster:AssociatedJobs')), getString(message('parallel:cluster:Cluster')), helpString));
            end
        end
        
    end
end
