function varargout = dbeval(d,varargin)
% dbeval  Evaluate expression in specified database.
%
% Syntax
% =======
%
%     [VALUE,VALUE,...] = dbeval(D,EXPRESSION,EXPRESSION,...)
%     [VALUE,VALUE,...] = dbeval(M,EXPRESSION,EXPRESSION,...)
%
%
% Syntax with steady-state references
% ====================================
%
%     [VALUE,VALUE,...] = dbeval(D,SS,EXPRESSION,EXPRESSION,...)
%
% Input arguments
% ================
%
% * `D` [ struct ] - Input database within which the expressions will be
% evaluated.
%
% * `M` [ model ] - Model object whose steady-state database will be used
% to evaluate the expression.
%
% * `EXPRESSION` [ char ] - Expression that will be evaluated using the
% fields of the input database.
%
% Output arguments
% =================
%
% * `value` [ ... ] - Resulting value.
%
% Description
% ============
%
% Example
% ========
%
% Create a database with two fields and one subdatabase with one field,
%
%     d = struct();
%     d.a = 1;
%     d.b = 2;
%     d.dd = struct();
%     d.dd.c = 3;
%     display(d)
%     d =
%        a: 1
%        b: 2
%        c: [1x1 struct]
%
% Use the `dbeval` function to evaluate an expression within the database
%
%     dbeval(d,'a+b+dd.c')
%     ans =
%           7
%

% -IRIS Toolbox.
% -Copyright 2007-2012 Jaromir Benes.

if ~isempty(varargin) ...
        && (isstruct(varargin{1}) || isa(varargin{1},'metaobj'))
    ss = varargin{1};
    varargin(1) = [];
else
    ss = struct([]);
end

% Parse required input arguments.
ip = inputParser();
ip.addRequired('D',@(x) isstruct(x) || ismodel(x));
ip.addRequired('SS',@(x) isstruct(x) || ismodel(x));
ip.addRequired('EXPRESSION', ...
    @(x) isempty(x) || iscellstr(x{1}) || iscellstr(x));
ip.parse(d,ss,varargin);

if isempty(varargin)
    varargout = {};
    return
elseif iscellstr(varargin{1})
    expr = varargin{1};
    multiple = false;
else
    expr = varargin;
    multiple = true;
end

if isa(d,'model')
    d = get(d,'sstateLevel');
end

if isa(ss,'model')
    ss = get(ss,'sstateLevel');
end

%**************************************************************************

expr = strtrim(expr);
list = fieldnames(d).';
list2 = fieldnames(ss).';
prefix = [char(0),'.'];
prefix2 = [char(1),'.'];
for i = 1 : length(list2)
    expr = regexprep(expr,['&\<',list2{i},'\>'],[prefix2,list2{i}]);
end
for i = 1 : length(list)
    expr = regexprep(expr,['(?<!\.)\<',list{i},'\>'],[prefix,list{i}]);
end

expr = strrep(expr,prefix,'d.');
expr = strrep(expr,prefix2,'ss.');

% Replace all possible assignments and equal signs used in IRIS codes.
% Non-linear simulation earmarks.
expr = strrep(expr,'==','=');
expr = strrep(expr,'=#','=');
% Dtrend equations.
expr = strrep(expr,'+=','=');
expr = strrep(expr,'*=','=');
% Identities.
expr = strrep(expr,':=','=');

% Convert x=y and x+=y into x-(y) so that we can evaluate LHS minus RHS.
% Note that using strrep is faster than regexprep.
index = strfind(expr,'=');
for i = 1 : length(index)
    if length(index{i}) == 1
        % Remove trailing colons.
        if expr{i}(end) == ';'
            expr{i}(end) = '';
        end
        expr{i} = [expr{i}(1:index{i}-1),'-(',expr{i}(index{i}+1:end),')'];
    end
end

varargout = cell(size(expr));
for i = 1 : length(expr)
    try
        varargout{i} = eval(expr{i});
    catch
        varargout{i} = NaN;
    end
end

if ~multiple
    varargout{1} = varargout;
    varargout(2:end) = [];
end

end