function varargout = dbsearchuserdata(d,varargin)
% dbsearchuserdata  Search database to find tseries by matching the content of their userdata fields.
%
% Syntax
% =======
%
%     LIST = dbsearchuserdata(D,FIELD1,REGEXP1,FIELD2,REGEXP2,...)
%     LIST = dbsearchuserdata(D,FLAG,FIELD1,REGEXP1,FIELD2,REGEXP2,...)
%
% Input arguments
% ================
%
% * `D` [ struct ] - Input database whose tseries fields will be searched.
%
% * `FLAG` [ '-all' | '-any' ] - Specifies if all conditions or any one
% condition must be met for the series to pass the test; if not
% specified, `'-all'` is assumed.
%
% * `FIELD1`, `FIELD2`, ... [ char ] - Names of fields in the userdata
% struct.
%
% * `REGEXP1`, `REGEXP2`, ... [ char ] - Regular expressions against which
% the respective userdata fields will be matched.
%
% Output arguments
% =================
%
% * `LIST` [ cellstr ] - Names of tseries that pass the test.
%
% * `D` [ struct ] - Output database with only those tseries that pass the
% test.
%
% Description
% ============
%
% For a successful match, the userdata must be a struct, and the tested
% fields must be text strings.
%
% Use an equal sign, `=`, after the name of the userdata fields in
% `FIELD1`, `FIELD2`, etc. to request a case-insensitive match, and an
% equal-shart sign, `=#`, to indiciate a case-sensitive match.
%
% Example
% ========
%
%     [list,dd] = dbsearchuserdata(d,'.DESC=','Exchange rate','.SOURCE=#','IMF');
%
% Each individual tseries object in the database `D` will be tested for two
% conditions:
%
% * whether its user data is a struct including a field named `DESC`, and
% the field contains a string `'Exchange rate'` in it (case insensitive,
% e.g. `'eXcHaNgE rAtE'` will also be matched);
%
% * whether its user data is a struct including a field named `SOURCE`, and
% the field contains a string `'IMF'` in it (case sensitive, e.g. `'Imf'`
% will not be matched).
%
% All tseries object that pass both of these conditions are returned in the
% `LIST` and the output database `D`.

% -IRIS Toolbox.
% -Copyright (c) 2007-2012 Jaromir Benes.


func = @(x) all(x);
if ~isempty(varargin)
    if (isequal(varargin{1},'-all') || isequal(varargin{1},'-any'))
        func = str2func(['@',varargin{1}(2:end)]);
        varargin(1) = [];
    end
end

dotest();

% Output arguments.
varargout{1} = list;
if nargout > 1
    varargout{2} = d * list;
end

% Nested functions.

%**************************************************************************
    function dotest()
        list = fieldnames(d).';
        nlist = length(list);
        
        testfield = {};
        testvalue = {};
        testfunc = {};
        dopreparetests();
        ntest = length(testfield);
        
        matched = false(1,nlist);
        for i = 1 : nlist
            name = list{i};
            if isa(d.(name),'tseries')
                x = userdata(d.(name));
                if ~isstruct(x)
                    continue
                end
                doevaluatetests();
            end
        end
        list = list(matched);
        
        function dopreparetests()
            varargin(1:2:end) = strtrim(varargin(1:2:end));
            for ii = 1 : 2 : length(varargin)
                if isempty(varargin{ii}) || isempty(varargin{ii+1})
                    continue
                end
                testfield{end+1} = varargin{ii}; %#ok<AGROW>
                testvalue{end+1} = varargin{ii+1}; %#ok<AGROW>
                testfunc{end+1} = @regexpi; %#ok<AGROW>
                if ~isempty(testfield{end}) && testfield{end}(end) == '#'
                    testfield{end}(end) = '';
                    testfunc{end} = @regexp;
                end
                if ~isempty(testfield{end}) && testfield{end}(end) == '='
                    testfield{end}(end) = '';
                end
                if ~isempty(testfield{end}) && testfield{end}(1) == '.'
                    testfield{end}(1) = '';
                end
            end
        end
        % dopreparetests().

        function doevaluatetests()
            xlist = cell(1,0);
            if isstruct(x)
                xlist = fieldnames(x);
            end
            xmatched = false(1,ntest);
            for ii = 1 : ntest
                index = strcmp(testfield{ii},xlist);
                if ~any(index)
                    xmatched(ii) = false;
                    continue
                end
                if isempty(testvalue{ii})
                    xmatched(ii) = true;
                    continue
                end
                xvalue = x.(xlist{index});
                if ~ischar(xvalue)
                    continue
                end
                xmatched(ii) = ...
                    ~isempty(testfunc{ii}(xvalue,testvalue{ii},'once'));
            end
            matched(i) = func(xmatched);
        end
        % doevaluatetests().
        
    end
% dotest().

end