classdef modelfile < report.userinputobj
    % modelfile  Write formatted model file in report.
    %
    % Syntax
    % =======
    %
    %     P.modelfile(CAP,FILENAME,...)
    %     P.modelfile(CAP,FILENAME,M,...)
    %
    % Input arguments
    % ================
    %
    % * `P` [ report ] - Report object created by the
    % [`report.new`](report/new) function.
    %
    % * `CAP` [ char | cellstr ] - Title and subtitle displayed at the top
    % of the table.
    %
    % * `FILENAME` [ char ] - Model file name.
    %
    % * `M` [ model ] - Model object from which the values of parameters and
    % std devs of shocks will be read; if missing no parameter values or std
    % devs will be printed.
    %
    % Options
    % ========
    %
    % * `'lines='` [ numeric | *`Inf`* ] - Print only selected lines of the model
    % file `FILENAME`; Inf means all lines will be printed.
    %
    % * `'lineNumbers='` [ *`true`* | `false` ] - Display line numbers.
    %
    % * `'footnote='` [ char | *empty* ] - Footnote at the model file title;
    % only shows if the title is non-empty.
    %
    % * `'paramValues='` [ *`true`* | `false` ] - Display the values of parameters
    % and std devs of shocks next to each occurence of a parameter or a shock;
    % this option works only if a model object `M` is entered as the 3rd input
    % argument.
    %
    % * `'syntax='` [ *`true`* | `false` ] - Highlight model file syntax; this
    % includes model language keywords, descriptions of variables, shocks and
    % parameters, and equation labels.
    %
    % * `'typeface='` [ char | *empty* ] - (Not inheritable from parent
    % objects) LaTeX code specifying the typeface for the model file as a
    % whole; it must use the declarative forms (such as `\itshape`) and not the
    % command forms (such as `\textit{...}`).
    %
    % Description
    % ============
    %
    % If you enter a model object with multiple parameterisations, only the
    % first parameterisation will get reported.
    %
    % At the moment, the syntax highlighting in model file reports does not
    % handle correctly comment blocks, i.e. `%{ ... %}`.
    %
    % Example
    % ========
    %

    % -IRIS Toolbox.
    % -Copyright (c) 2007-2012 Jaromir Benes.
    
    properties
        filename = '';
        modelobj = [];
    end
    
    methods
        
        function THIS = modelfile(varargin)
            THIS = THIS@report.userinputobj(varargin{:});
            THIS.childof = {'report'};
            THIS.default = [THIS.default,{ ...
                'linenumbers',true,@islogicalscalar,true, ...
                'lines',Inf,@isnumeric,true, ...
                'paramvalues',true,@islogicalscalar,true, ....
                'separator','',@ischar,false, ...
                'syntax',true,@islogicalscalar,true, ...
                'typeface','',@ischar,false, ...                
                }];
        end
        
        function [THIS,varargin] = specargin(THIS,varargin)
            if ~isempty(varargin) && ischar(varargin{1})
                THIS.filename = varargin{1};
                varargin(1) = [];
            end
            if ~isempty(varargin) && isa(varargin{1},'metaobj')
                THIS.modelobj = varargin{1};
                varargin(1) = [];
            end
        end
        
        function [C,TEMPS] = speclatexcode(THIS)
            % Do not do verbatim because we use \verb direct in the typeset model file.
            THIS.options.verbatim = false;
            THIS.options.centering = false;
            THIS.userinput = printmodelfile(THIS);
            [C,TEMPS] = speclatexcode@report.userinputobj(THIS);
        end
        
    end
    
    methods (Access=protected,Hidden)
        
        function C = printmodelfile(THIS)
            % TODO: Check for '@' in the model file; if found replace the \verb
            % delimiter with something else or remove all @s from the model file.
            % TODO: Handle comment blocks %{...%} properly.
            C = '';
            if isempty(THIS.filename)
                return
            end
            ismodel = ~isempty(THIS.modelobj) && isa(THIS.modelobj,'metaobj');
            if ismodel
                plist = get(THIS.modelobj,'plist');
                elist = get(THIS.modelobj,'elist');
            end
            br = sprintf('\n');
            
            line = file2char(THIS.filename,'cellstrl',THIS.options.lines);
            nline = length(line);
            if isinf(THIS.options.lines)
                THIS.options.lines = 1 : nline;
            end
            ndigit = ceil(log10(max(THIS.options.lines)));
            
            C = [C,'\definecolor{mylabel}{rgb}{0.55,0,0.35}',br];
            C = [C,'\definecolor{myparam}{rgb}{0.90,0,0}',br];
            C = [C,'\definecolor{mykeyword}{rgb}{0,0,0.75}',br];
            C = [C,'\definecolor{mycomment}{rgb}{0,0.50,0}',br];
            
            line = strrep(line,char(10),'');
            line = strrep(line,char(13),'');
            for i = 1 : nline
                % Split the line if there is a line comment.
                tok = regexp(line{i},'([^%]*)(%.*)?','tokens','once');
                if ~isempty(tok)
                    x = tok{1};
                    y = tok{2};
                else
                    x = '';
                    y = '';
                end
                x = dosyntax(x);
                y = docomments(y);
                C = [C,x,y,' \\',br]; %#ok<AGROW>
            end
            
            function C = dosyntax(C)
                
                keywords = @dokeywords; %#ok<NASGU>
                labels = @dolabels; %#ok<NASGU>
                paramvalues = @doparamvalues; %#ok<NASGU>
                
                if THIS.options.syntax
                    C = regexprep(C, ...
                        '!!|!\<\w+\>|=#|&\<\w+>|\$.*?\$', ...
                        '${keywords($0)}');
                    C = regexprep(C, ...
                        '([''"])([^\n]*?)\1', ...
                        '${labels($1,$2)}');
                end
                if ismodel && THIS.options.paramvalues
                    % Find words not preceeded by an !; whether they really are parameter names
                    % or std errors is verified within paramvalues.
                    C = regexprep(C, ...
                        '(?<!!)\<\w+\>', ...
                        '${paramvalues($0)}');
                end
                if THIS.options.linenumbers
                    C = [ ...
                        sprintf('%*g: ',ndigit,THIS.options.lines(i)), ...
                        C];
                end
                C = ['\verb@',C,'@'];
                
                function C = dokeywords(C)
                    if strcmp(C,'!!') || strcmp(C,'=#') ...
                            || strncmp(C,'&',1) || strncmp(C,'$',1)
                        color = 'red';
                    else
                        color = 'mykeyword';
                    end
                    C = latex.stringsubs(C);
                    C = ['\textcolor{',color,'}{\texttt{',C,'}}'];
                    C = ['@',C,'\verb@'];
                end
                
                function C = dolabels(QMARK,C)
                    C = latex.stringsubs(C);
                    C = ['\textcolor{mylabel}{\texttt{',QMARK,C,QMARK,'}}'];
                    C = ['@',C,'\verb@'];
                end
                
                function C = doparamvalues(C)
                    if any(strcmp(C,elist))
                        value = THIS.modelobj.(['std_',C]);
                        prefix = '\sigma\!=\!';
                    elseif any(strcmp(C,plist))
                        value = THIS.modelobj.(C);
                        prefix = '';
                    else
                        return
                    end
                    value = sprintf('%g',value(1));
                    value = strrep(value,'Inf','\infty');
                    value = strrep(value,'NaN','\mathrm{NaN}');
                    value = ['\raisebox{0em}{\color{myparam}$\left<{', ...
                        prefix,value,'}\right>$}'];
                    C = [C,'@',value,'\verb@'];
                end
                
            end
            
            function C1 = docomments(C)
                C1 = '{';
                if THIS.options.syntax
                    C1 = [C1,'\color{mycomment}'];
                end
                C1 = [C1,'\verb@',C,'@}'];
            end
            
        end
        
    end
    
end