% HPCServerClusterProperties - Class for holding properties defined by the HPC Server cluster

%   Copyright 2014-2018 The MathWorks, Inc.

classdef HPCServerClusterProperties

    properties (SetAccess = immutable)
        Filename
        Names
        Values
    end
    
    methods
        function obj = HPCServerClusterProperties(filename)
            obj.Filename = filename;
            [obj.Names, obj.Values] = iReadPropertyFile(filename);
        end
    end
end

%----------------------------------------------
function [props, vals] = iReadPropertyFile(filename)
import parallel.internal.customattr.PropSet

validateattributes(filename, {'char'}, {'row'});
if ~exist(filename, 'file')
    error(message('parallel:cluster:HPServerUtilsFailedToFindPropertiesFile', filename));
end

fid = fopen(filename, 'r');
if fid < 0
    error(message('parallel:cluster:HPServerUtilsFailedToOpenPropertiesFile', filename));
end
closeFile = onCleanup(@() fclose(fid));

% The file uses '#' for comments and has the format:
% <PROPERTY>=<VALUE>
% The values may contain spaces.
propsAndVals = textscan(fid, '%s%s', 'delimiter', '=', 'commentstyle', '#');
if length(propsAndVals{1}) ~= length(propsAndVals{2})
    error(message('parallel:cluster:HPCServerUtilsInvalidPropertyFile', filename));
end

propsInFile = propsAndVals{1};
valsInFile = propsAndVals{2};

% Strip off leading and trailing whitespace from the props and values
propsInFile = strtrim(propsInFile);
valsInFile = strtrim(valsInFile);

% Convert the properties to API2 properties and values 
[props, vals] = iConvertPropsInPropertyFile(propsInFile, valsInFile);
% Make sure only 1 value exists for each prop
pvPairs = PropSet.namesValuesToPv(props, vals);
[props, vals] = PropSet.amalgamate({}, pvPairs{:});

end


%----------------------------------------------
function [props, vals] = iConvertPropsInPropertyFile(propsInFile, valsInFile)
validateattributes(propsInFile, {'cell'}, {'numel', numel(valsInFile)});
validateattributes(valsInFile, {'cell'}, {});

persistent propConverter
if isempty(propConverter)
    noop = @(x) x;
    propConverter = { ...
        % prop name in file,             API2 prop name,        value conversion Fcn
        'CLUSTER_MATLAB_ROOT',           'ClusterMatlabRoot',   noop; ...
        'NUMBER_MATLAB_WORKER_LICENSES', 'NumWorkers',          @str2double;...
        'JOB_TEMPLATE',                  'JobTemplate',         noop;...
        'DATA_LOCATION_ROOT',            'JobStorageLocation',  @iGetDataLocationFromRoot;...
        'USE_SOA_JOB_SUBMISSION',        'UseSOAJobSubmission', @iStringToLogical; ...
        'CLUSTER_NAME',                  'Name',                noop; ...
        'USE_ONLINE_LICENSING',          'RequiresOnlineLicensing', @iStringToLogical; ...
        };
end    

props = cell(size(propsInFile));
vals = cell(size(propsInFile));
failedToConvertIdx = false(size(propsInFile));
for ii = 1:numel(propsInFile)
    propIdx = strcmp(propConverter(:, 1), propsInFile{ii});
    props{ii} = propConverter{propIdx, 2};
    convertFcn = propConverter{propIdx, 3};
    try
        vals{ii} = convertFcn(valsInFile{ii});
    catch err %#ok<NASGU>
        failedToConvertIdx(ii) = true;
    end
end

% Warn and remove any props that we failed to convert
if any(failedToConvertIdx)
    failedToConvertProps = propsInFile(failedToConvertIdx);
    warning(message('parallel:cluster:HPCServerUtilsFailedToConvertValuesInPropertyFile', strtrim( sprintf( '%s ', failedToConvertProps{ : } ) )));
    props(failedToConvertIdx) = [];
    vals(failedToConvertIdx) = [];
end
end

%---------------------------------------------------------
function dataLocation = iGetDataLocationFromRoot(dataLocationRoot)
import parallel.internal.apishared.HPCServerUtils
if isempty(dataLocationRoot)
    dataLocation = dataLocationRoot;
    return;
end

% The username is appended to the dataLocationRoot to give the JobStorageLocation
[~, username] = HPCServerUtils.getUsername();
dataLocation = fullfile(dataLocationRoot, username);
end

%---------------------------------------------------------
function val = iStringToLogical(str)
val = logical(str2double(str));
end




