% ProfileExpander - Expand a profile into the PV pairs of different objects
% Optimized for performance when the same profile is expanded multiple times,
% if that profile's values have not changed.
 
%   Copyright 2011 The MathWorks, Inc.

classdef (Hidden, Sealed) ProfileExpander
    methods (Static)
        function clusterType = getClusterType(profileName)
        % clusterType = getClusterType(profileName) - get 
        % the cluster type of the cluster object defined by the specified profile
            clusterType = iGetAllProfileInfo(profileName);        
        end

        function [names, values] = getClusterProperties(profileName)
        % [names, values] = getClusterProperties(profileName) - get 
        % the cluster PV pairs defined by the specified profile
            [~, names, values] = iGetAllProfileInfo(profileName);        
        end
        
        function [names, values] = getJobProperties(profileName)
        % [names, values] = getJobProperties(profileName) - get the job PV pairs
        % defined by the specified profile.  Does not filter out PV pairs
        % by scheduler type.
            [~, ~, ~, names, values] = iGetAllProfileInfo(profileName);
        end
        
        function [names, values] = getTaskProperties(profileName)
        % [names, values] = getTaskProperties(profileName) - get the task PV pairs
        % defined by the specified profile.  Does not filter out PV pairs
        % by scheduler type.
            [~, ~, ~, ~, ~, names, values] = iGetAllProfileInfo(profileName);
        end
    end
end

%---------------------------------------------------------------------
function [clusterType, clusterProps, clusterVals, jobProps, jobVals, taskProps, taskVals] = iGetAllProfileInfo(profileName)
persistent cachedProfileName
persistent cachedInfo

validateattributes(profileName, {'char'}, {'row'});
if ~isempty(cachedProfileName) && strcmp(profileName, cachedProfileName)
    clusterType  = cachedInfo{1};
    clusterProps = cachedInfo{2};
    clusterVals  = cachedInfo{3};
    jobProps     = cachedInfo{4};
    jobVals      = cachedInfo{5};
    taskProps    = cachedInfo{6};
    taskVals     = cachedInfo{7};
    return;
end

[profile, schedComp, projComp] = iGetComponentsFromName(profileName);
[clusterType, clusterProps, clusterVals, jobProps, jobVals, taskProps, taskVals] = ...
    iExtractInfo(schedComp, projComp);

% Attach listeners to reset the cached values if the profile changes or is deleted.  Use
% event.listener so the listener lifetime is handled in here.
% SettingsNodeChanged event will be fired if the underlying settings node is deleted
% (e.g. from the UI) and ObjectBeingDestroyed will be fired if the delete() method
% is called on the profile/component.
eventsToListen = {'SettingsNodeChanged', 'ObjectBeingDestroyed'};
listenerFcn = @(~, ~) nResetCachedValues;
profileListeners      = cellfun(@(x) event.listener(profile, x, listenerFcn), eventsToListen, ...
                               'UniformOutput', false);
schedCompListeners    = cellfun(@(x) event.listener(schedComp, x, listenerFcn), eventsToListen, ...
                               'UniformOutput', false);
if isempty(projComp)
    projCompListeners = {event.listener.empty()};
else
    projCompListeners = cellfun(@(x) event.listener(projComp,  x, listenerFcn), eventsToListen, ...
                               'UniformOutput', false);
end
listeners            = [profileListeners{:}, schedCompListeners{:}, projCompListeners{:}];

% Stash the values in the cache.  Make sure we keep hold of the listeners
% so that they actually fire.
cachedProfileName = profileName;
cachedInfo        = {clusterType, clusterProps, clusterVals, ...
    jobProps, jobVals, taskProps, taskVals, listeners};

    function nResetCachedValues
    % Reset the cached values and clear the listeners
        cachedProfileName = '';
        cachedInfo        = {};
    end
end

%---------------------------------------------------------------------
function [profile, schedComp, projComp] = iGetComponentsFromName(profileName)
% Get the profile and extract the components
p = parallel.Settings;
profile = p.findProfile('Name', profileName);
if isempty(profile)
    p.hThrowNonExistentProfile(profileName);
end
schedComp = profile.getSchedulerComponent();
projComp = profile.getProjectComponent();
end

%---------------------------------------------------------------------
function [clusterType, clusterProps, clusterVals, jobProps, jobVals, taskProps, taskVals] = iExtractInfo(schedComp, projComp)
import parallel.internal.customattr.PropSet
% Get the props out of the components
[clusterProps, clusterVals] = schedComp.hGetClusterPVPairs();
if isempty(projComp)
    jobProps  = {}; jobVals   = {};
    taskProps = {}; taskVals  = {};
else
    [jobProps, jobVals]   = projComp.hGetJobPVPairs(schedComp.Type);
    [taskProps, taskVals] = projComp.hGetTaskPVPairs(schedComp.Type);
end
[jobProps, jobVals]   = schedComp.hGetJobPVPairs(jobProps, jobVals);
[taskProps, taskVals] = schedComp.hGetTaskPVPairs(taskProps, taskVals);

clusterType = schedComp.TypeEnum;
end