% s = ALLSELECTORS(blockH)
%       returns all selectors for a block
% See also: SLCOVERAGE.BLOCKSELECTOR, SLCOVERAGE.METRICSELECTOR, 
%           SLCOVERAGE.SFCNSELECTOR, SLCOVERAGE.SELECTOR

% Copyright 2017-2020 The MathWorks, Inc.

function selectors = allSelectors(blockH, varargin)

    [status, msgId] = SlCov.CoverageAPI.checkCvLicense;
    if status == 0
        error(message(msgId));
    end

    
    params = parseArgs(varargin{:});
    isStateflow = false;
    isModel = false;
    isMatlabScript = false;
    selectors = [];
    try
        if ~Simulink.ID.isValid(blockH)
            sid = Simulink.ID.getSID(blockH);
        else
            sid = blockH;
        end
        isStateflow = contains(class(Simulink.ID.getHandle(sid)),'Stateflow');
        isModel = ~isStateflow && strcmpi(get_param(sid, 'type'), 'block_diagram');
    catch
        if ischar(blockH) && endsWith(blockH, '.m')
            sid = blockH;
            isMatlabScript = true;
        else
            return;
        end
    end
    forXIL = ~strcmpi(params.SimulationMode, 'normal');
    isSFcn = false;
    if ~forXIL && ~isModel && ~isStateflow && ~isMatlabScript && strcmpi(get_param(sid, 'BlockType'), 's-function')
        isSFcn = true;
        if checkFilterType(params, 'slcoverage.SFcnSelectorType')
            checkCompile(sid);
            selectors = slcoverage.Selector.possibleCodeSelector(sid, 'IncludeOutcomes', params.IncludeCodeOutcomes);
        end
    end  
    if ~forXIL && ~isModel && checkFilterType(params, 'slcoverage.BlockSelectorType')
        props = SlCov.FilterEditor.deriveProperties(sid);
        for ii = 1:numel(props)
            cp = props(ii);
            description = SlCov.FilterEditor.getPropertyDescription(cp);
            description  = cvi.ReportUtils.html_to_str(description);
            type  = cp.selectorType;
            
            if ~isa(type, 'slcoverage.BlockSelectorType')
                continue;
            end
            
            value = cp.value;
            if type == slcoverage.BlockSelectorType.TemporalEvent
                value = value.name;
            end
            tr = slcoverage.BlockSelector(type, value);
            tr.setDescription(description);

            selectors = addSelector(selectors, tr);
        end
    end
    if ~forXIL && ~isModel && ~isSFcn && checkFilterType(params, 'slcoverage.MetricSelectorType')
        if ~isMatlabScript 
            checkCompile(sid);
        end
        props = SlCov.FilterEditor.deriveMetricProperties(sid);
        for ii = 1:numel(props)
            type  = props(ii).selectorType;
            description = props(ii).value.valueDesc;
            idx = props(ii).value.idx;
            outcomeIdx = props(ii).value.outcomeIdx;

            tr = slcoverage.MetricSelector(type, sid, idx, outcomeIdx);
            tr.setDescription(description);
            
            selectors = addSelector(selectors, tr);
        end
    end

    % Code selectors are extracted at the model level only
    if isModel && checkFilterType(params, 'slcoverage.CodeSelectorType')
        if forXIL
            codeTrFile = slcoverage.Selector.checkCodeCompile(sid, params.SimulationMode);
            tr = slcoverage.Selector.possibleCodeSelector(sid,...
                'IncludeOutcomes', params.IncludeCodeOutcomes,...
                'Mode', 'xil',...
                'CodeTr', codeTrFile);
            if ~isempty(tr)
                selectors = addSelector(selectors, tr);
            end
        elseif SlCov.isSLCustomCodeCovFeatureOn()
            checkCompile(sid, true);
            tr = slcoverage.Selector.possibleCodeSelector(sid, ...
                'IncludeOutcomes', params.IncludeCodeOutcomes, ...
                'Mode', 'slcc');
            if ~isempty(tr)
                selectors = addSelector(selectors, tr);
            end
        end
    end
    selectors = checkAllParams(params, selectors);
end
%===============
function setParam(modelH, param, value)
%override configset reference: g3247158
    if isa(getActiveConfigSet(modelH), 'Simulink.ConfigSetRef')
        configset.reference.overrideParameter(modelH,param, value);
    else
        set_param(modelH,param, value);
    end
end
%===============
function checkCompile(sid, doubleCheck)

    if nargin < 2
        doubleCheck = false;
    end
    
    model = Simulink.ID.getModel(sid);
    cm = SlCov.CoverageAPI.findModelcov(model);

    doCovComp = (isempty(cm) || SlCov.CovMode.isGeneratedCode(cv('get', cm, '.simMode')));
    if ~doCovComp || doubleCheck
        coveng = cvi.TopModelCov.getInstance(model);
        doCovComp = isempty(coveng);
    end

    if doCovComp
        modelH = get_param(model, 'handle');
        prevDirty = get_param(modelH, 'Dirty');
        restoreDirtyFlag = onCleanup(@() set_param(modelH, 'Dirty', prevDirty));
        pen = get_param(modelH, 'CovEnable');
        psc = get_param(modelH, 'CovScope');
        setParam(modelH, 'CovEnable', 'on');
        setParam(modelH, 'CovScope', 'EntireSystem');
        SlCov.CoverageAPI.compileForCoverage(model);
        setParam(modelH, 'CovEnable', pen);
        setParam(modelH, 'CovScope', psc);
    end
end

%===============
function out = checkAllParams(params, selectors)
    out = [];
    for idx = 1:numel(selectors)
        sel = selectors(idx);
        res = true;
        if ~isempty(params.Description)
            res = res && contains(sel.Description, params.Description);
        end
        if ~isempty(params.Type)
            res = res && any(contains(convertEnumToString(sel.Type), params.Type));
        end
        if res
            out = addSelector(out, sel);
        end
    end
end
%===============
function res = checkFilterType(params, ft)
    res = true;
    if ~isempty(params.Type)
        res = res && any(contains(params.Type, ft));
    end
end
%===============
function res = convertEnumToString(enum)
    if ischar(enum) || isstring(enum)
        res = validatestring(enum, slcoverage.Selector.SupportedSelectorTypes);
    else
        res = [class(enum) '.' char(enum)];
    end
end

%===============
function params = parseArgs(varargin)
    params.Type = [];
    params.Description = '';
    params.SimulationMode = 'normal';
    params.IncludeCodeOutcomes = true;
    if isempty(varargin)
        return;
    end
    p = inputParser;
    typeValidationFcn = @(x)validateattributes(x, slcoverage.Selector.SupportedSelectorTypes, {'scalar'});
    p.addParameter('Type', typeValidationFcn);
    descriptionValidationFcn = @(x)validateattributes(x, {'char'}, {'vector'});
    logicalValidationFcn = @(x)validateattributes(x, {'logical', 'numeric'}, {'scalar', '>=', 0, '<=', 1});
    p.addParameter('Description', descriptionValidationFcn );
    p.addParameter('SimulationMode', @(x)validatestring(x, [{'normal'}, slcoverage.Selector.SupportedXILModes]));
    p.addParameter('IncludeCodeOutcomes', logicalValidationFcn);
    parse(p,varargin{:});
    if ~any(contains(p.UsingDefaults, 'Type'))
        params.Type = p.Results.Type;
        if iscell(params.Type)
            for idx = 1:numel(params.Type)
                params.Type{idx} = convertEnumToString(params.Type{idx});
            end
        else
            params.Type = convertEnumToString(params.Type);
        end
    end
    if ~any(contains(p.UsingDefaults, 'Description'))
        params.Description = p.Results.Description;
    end
    if ~any(contains(p.UsingDefaults, 'SimulationMode'))
        params.SimulationMode = char(p.Results.SimulationMode);
    end
    if ~any(contains(p.UsingDefaults, 'IncludeCodeOutcomes'))
        params.IncludeCodeOutcomes = logical(p.Results.IncludeCodeOutcomes);
    end
    if ~SlCov.isCodeOutcomeFilterFeatureOn()
        params.IncludeCodeOutcomes = false;
    end
end
%===============
function s = addSelector(s, tr)
    if isempty(s)
        s = tr;
    else
        s(end + 1) = tr;
    end
end

