function updateDefines(h, defs, varargin)
% UPDATEDEFINES - The updateDefines function updates the value of specified
% preprocessor macro definitions to the new value.  If the define does not
% exist, then the define is ignored, unless the param val pair "create, true"
% is passed in.

%   Copyright 2012-2019 The MathWorks, Inc.

    [argsOK, args] = locCheckArgs(defs, varargin{:});
    if ~(argsOK)
        DAStudio.error('RTW:buildInfo:badMethodArguments',...
                       'RTW.BuildInfoOptions.updateDefines');
    end

    curKeys = get(h.Options.Defines,'Key');
    
    [upKeys, upIdx] = ismember(args.keys,curKeys);
    % indices will be 0 if the arg.keys{i} is not in cur_keys, remove those
    % entries using the upKeys t/f vector
    upIdx = upIdx(upKeys);
    
    % update the values of the defines
    vals = reshape(args.vals(upKeys),[],1);
    set(h.Options.Defines(upIdx),{'Value'},vals)
    
    % create new entries for any defines that are 'new' if the caller wanted to
    % create them.
    if args.create
        [newKeys,newIdx] = setdiff(args.keys,curKeys);
        if ~isempty(newKeys)
            h.addDefines(defs(newIdx));
        end
    end
end

function [argsOK, args] = locCheckArgs(defs, varargin)

    persistent parserObj;
    argsOK = true;
    if isempty(parserObj)
        % elements for the input parser are:
        %
        %  {{fieldname1, default, validator func}
        %   {fieldname2, default, validator func}
        %   {fieldname3, default, validator func}}
        
        pFields =...
            {{'create', false,  @islogical}...
            };
        
        [args, parserObj] = locParseInput(pFields,varargin{:});
    else
        args = locParseInput(parserObj,varargin{:});     
    end

    [keys,vals] = locResolveDefines(defs);
    
    [args.keys,idx] = RTW.unique(keys);
    args.vals = vals(idx);
end

function [args, parserObj] = locParseInput(pFields, varargin)
    parserObj = [];
    if isa(pFields,'inputParser')
        p = pFields;
    else
        p = inputParser;
        p.KeepUnmatched = true;
        p.StructExpand  = true;
        p.CaseSensitive = false; 

        for i=1:length(pFields)
            p.addParamValue(pFields{i}{1}, pFields{i}{2}, pFields{i}{3});
        end
        parserObj = p;
    end
    
    p.parse(varargin{:});
    args = p.Results;
    locErrorIfAdditionalArgs(p.Unmatched);
end

function locErrorIfAdditionalArgs(unmatched)

    f = fields(unmatched);
    if isempty(f)
        return;
    end
    badFields = sprintf('%s\n',f{:});
    
    DAStudio.error('RTW:buildInfo:UnknownArgs', 'updateDefines', badFields);
end

function  [Keys,Vals] = locResolveDefines(Options)
% first strip off any -d or -D at the beginning
    defs = regexprep(Options,'^-[dD]?(.*)','$1');
    
    % this call grabs anything up to but not including the first '='
    tmp = regexp(defs,'([^=]*)=?','tokens','once');
    Keys = [tmp{:}];
    
    % if there is an '=' in the define, this grabs everything after the
    % first '='.  Otherwise it will return an empty string.  So if the
    % following vector is passed in, the resulting vector of values is
    % shown:
    % 
    % defs = {'FOO' 'BAR=1' ABC="xx=123"'
    %
    % resulting values:
    % 
    % Vals = {'' '1' '"xx=123"'}
    %    
    tmp =  regexp(defs,'[^=]*=?(.*)','tokens','once');
    Vals = [tmp{:}];
end
