function d = reporting(input,d,range,varargin)
% reporting  Run reporting equations.
%
% Syntax
% =======
%
%     D = reporting(FNAME,D,RANGE,...)
%
% Input arguments
% ================
%
% * `FNAME` [ char ] - Name of the reporting equation file.
%
% * `D` [ struct ] - Input database that will be used to evaluate the
% reporting equations.
%
% * `RANGE` [ numeric ] - Date range on which the reporting equations will
% be evaluated.
%
% Output arguments
% =================
%
% * `D` [ struct ] - Output database with reporting variables.
%
% Options
% ========
%
% * `'dynamic='` [ *`true`* | `false` ] - If `true` equations will be evaluated
% period by period allowing for own lags; if `false`, equations will be
% evaluated once for all periods.
%
% * `'merge='` [ *`true`* | `false` ] - Merge output database with input
% datase.
%
% Description
% ============
%

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

default = {...
    'dynamic',true,@islogical,...
    'merge',true,@islogical,...
    };
options = passvalopt(default,varargin{:});

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

if ischar(input)
    % Preparse reporting equation file.
    p = preparser(input);
    % Save carry-around packages.
    preparser.export('reporting',p.Export);
    % Parser reporting code.
    eqtn = reporting(p);
elseif isstruct(input)
    % Reporting eqtn struct.
    eqtn = input;
else
    utils.error('reporting', ...
        'Invalid type of input argument(s).');
end

if ~isstruct(eqtn) || isempty(eqtn) || ~isfield(eqtn,'lhs') || isempty(eqtn.lhs)
    return
end

% Remove time subscripts from non-tseries field names in the equations.
list = fieldnames(d);
for i = 1 : length(list)
    if ~istseries(d.(list{i})) && ~any(strcmp(eqtn.lhs,list{i}))
        eqtn.rhs = strrep(eqtn.rhs,sprintf('d.%s(t,:)',list{i}),sprintf('d.%s',list{i}));
    end
end

% Pre-allocate time series and assign comments.
for i = 1 : length(eqtn.lhs)
    if ~isfield(d,eqtn.lhs{i})
        d.(eqtn.lhs{i}) = tseries();
    end
    if ~isempty(eqtn.label{i})
        d.(eqtn.lhs{i}) = comment(d.(eqtn.lhs{i}),eqtn.label{i});
    end
end

if options.dynamic
    % Evaluate equations recursively period by period.
    fn = cell(size(eqtn.rhs));
    for i = 1 : length(eqtn.rhs)
        fn{i} = str2func(['@(d,t)',eqtn.rhs{i}]);
    end
    range = range(:).';
    for t = range
        for i = 1 : length(eqtn.rhs)
            try
                x = fn{i}(d,t);
            catch %#ok<CTCH>
                x = NaN;
            end
            if ~isnumeric(x)
                x = eqtn.nan{i};
            else
                x(isnan(x)) = eqtn.nan{i};
            end
            tmpsize = size(d.(eqtn.lhs{i}));
            if length(tmpsize) == 2 && tmpsize(2) == 1 && length(x) > 1
                d.(eqtn.lhs{i}) = scalar2nd(d.(eqtn.lhs{i}),size(x));
            end
            d.(eqtn.lhs{i})(t,:) = x;
        end
    end
else
    % Evaluate equations once for all periods.
    for i = 1 : length(eqtn.rhs)
        eqtn.rhs = strrep(eqtn.rhs,'(t,:)','{range,:}');
        try
            x = eval(eqtn.rhs{i});
        catch Error
            x = NaN;
            warning('iris:reporting',...
                '*** Error evaluating ''%s''.\n*** Matlab says: %s',...
                eqtn.userRHS{i},Error.message);
        end
        d.(eqtn.lhs{i}) = x;
    end
end

if ~options.merge
    d = d * eqtn.lhs;
end

end

% Subfunctions.

%**************************************************************************
function this = scalar2nd(this,newsize)

thissize = size(this.data);
if length(thissize) > 2 || thissize(2) > 1
    return
end
n = prod(newsize(2:end));
this.data = this.data(:,ones(1,n));
this.data = reshape(this.data,[thissize(1),newsize(2:end)]);
this.Comment = this.Comment(1,ones(1,n));
this.Comment = reshape(this.Comment,[1,newsize(2:end)]);

end