% SLCOVERAGE.CODESELECTOR Code filter selector class
%   Selects Code for filtering by it properties.
%   so = slcoverage.CodeSelector(type, fileName, funName, expr, exprIdx) - creates a selector object
%       type     - slcoverage.CodeSelectorType
%       fileName - c++/c file name
%       funName  - c++/c function name
%       expr     - expression to filter
%       exprIdx  - expression index
%
% SLCOVERAGE.CODESELECTOR methods:
%   slcoverage.CodeSelector.allSelectors(model) - all CodeSelectors
%                                                 for a model
% See also: SLCOVERAGE.CODESELECTORTYPE, SLCOVERAGE.FILTERRULE,
%           SLCOVERAGE.FILTER

% Copyright 2018 The MathWorks, Inc.

classdef CodeSelector < slcoverage.Selector

    properties (SetAccess = private)
        FileName  = ''
        FunctionName = ''
        Expr = ''
    end

    properties (Hidden = true)
        ExprIndex = []
        IsDec = []
    end

    %% --------------------------------------------------------------------
    methods
        function this = CodeSelector(type, fileName, varargin)

            % Must have a valid Simulink Coverage license
            [status, msgId] = SlCov.CoverageAPI.checkCvLicense;
            if status == 0
                error(message(msgId));
            end

            % Check the input arguments
            narginchk(2, 5);
            validateattributes(type, {'slcoverage.CodeSelectorType'}, {'scalar'}, 'CodeSelector', 'type', 1);
            strArgValidFcn = @(arg, name, pos)validateattributes(arg, {'char', 'string'}, {'scalartext'}, 'CodeSelector', name, pos);
            strArgValidFcn(fileName, 'fileName', 2);
            fileName = convertStringsToChars(fileName);
            [varargin{:}] = convertStringsToChars(varargin{:});

            % Construct the object
            this@slcoverage.Selector('', type, '');
            this.FileName  = fileName;

            % Generate the construction command
            constructorCode = sprintf('slcoverage.CodeSelector(%s, ''%s''',...
                mat2str(type), fileName);

            argParser = inputParser;
            argParser.StructExpand = false;
            alwaysTrue = @(x)true;
            switch type
                case  {slcoverage.CodeSelectorType.Decision, slcoverage.CodeSelectorType.Condition}
                    exprIdxValidationFcn = @(x)validateattributes(x, {'numeric'}, {'positive', 'nonzero', 'scalar'}, 'CodeSelector', 'exprIdx', 5);
                    argParser.addRequired('funName', @(x)strArgValidFcn(x, 'funName', 3));
                    argParser.addRequired('expr', @(x)strArgValidFcn(x, 'expr', 4));
                    argParser.addRequired('exprIdx', exprIdxValidationFcn);
                    parse(argParser, varargin{:});

                    this.FunctionName = argParser.Results.funName;
                    this.Expr = argParser.Results.expr;
                    this.ExprIndex = argParser.Results.exprIdx;
                    this.IsDec = isequal(type, slcoverage.CodeSelectorType.Decision);
                    constructorCode = [constructorCode,...
                        sprintf(', ''%s'', ''%s'', %s', this.FunctionName, this.Expr, mat2str(this.ExprIndex))];

                case slcoverage.CodeSelectorType.Function
                    argParser.addRequired('funName', @(x)strArgValidFcn(x, 'funName', 3));
                    argParser.addOptional('expr', '', alwaysTrue);
                    argParser.addOptional('exprIdx', [], alwaysTrue);
                    parse(argParser, varargin{:});
                    this.FunctionName = argParser.Results.funName;
                    constructorCode = [constructorCode, sprintf(', ''%s''', this.FunctionName)];

                case slcoverage.CodeSelectorType.File
                    argParser.addOptional('funName', '', alwaysTrue);
                    argParser.addOptional('expr', '', alwaysTrue);
                    argParser.addOptional('exprIdx', [], alwaysTrue);
                    parse(argParser, varargin{:});
            end

            constructorCode = [constructorCode, ')'];
            this.setConstructorCode(constructorCode);
        end
    end

    %% --------------------------------------------------------------------
    methods  (Static = true)
        function s = allSelectors(modelH, varargin)
            s = slcoverage.Selector.allSelectors(modelH,...
                'Type', 'slcoverage.CodeSelectorType',...
                varargin{:});
        end
    end
end
