classdef ReportGenerator < handle
    %REPORTGENERATOR Class that handles report generation logic for
    %econModeler
    
    %Copyright 2017 The MathWorks, Inc.
    
    properties(Access = private)
        Session
        Listeners
    end
    
    methods
        function this = ReportGenerator(session)
            this.Session = session;
            this.Listeners{end+1} = event.listener(this.Session,'ReportItemsChosen',...
                @this.generateReport);
        end
    end
    
    methods(Access = private)
        function generateReport(this,~,e)
            import mlreportgen.report.*
            import mlreportgen.dom.*
            
            
            reportedItems = [];
            
            try
            
                %Create the report
                report = internal.EconModeler.EconAppReport(e.FilePath,e.FileType);

                %Title Page
                tp = TitlePage();
                tp.Title = getString(message('econ:econModeler:ReportName'));
                tp.Subtitle = getString(message('econ:econModeler:ReportSummary'));
                tp.Author = '';
                toolboxInfo = ver('econ');
                tp.Publisher = toolboxInfo.Name + " " + getString(message('econ:econModeler:Version')) + ...
                    " " +  toolboxInfo.Version + " " + toolboxInfo.Release;
                tp.PubDate = string(datetime('today'));
                add(report,tp);

                %Table of Contents
                toc = TableOfContents();
                toc.Title = getString(message('econ:econModeler:TableOfContents'));
                add(report,toc);

                %Get all plots and test figures
                allFigs = this.Session.getAllFigures';
                testIdx = arrayfun(@(x)isa(x,'internal.EconModeler.EconComponents.Figures.AbstractTestFigure'),allFigs);
                testFigs = allFigs(testIdx);
                [~,testOrder] = sort([testFigs.ReportRank]);
                testFigs = testFigs(testOrder);
                plotFigs = allFigs(~testIdx);
                [~,plotOrder] = sort([plotFigs.ReportRank]);
                plotFigs = plotFigs(plotOrder);

                %Generate each requested series, and sort them by rank
                requestedSeries = e.TimeSeries;
                timeSeries = internal.EconModeler.EconComponents.AbstractTimeSeries.empty(length(requestedSeries),0);
                for n = 1:length(requestedSeries)
                    timeSeries(n) = this.Session.getTimeSeries(requestedSeries{n});
                end
                [~,timeSeriesOrder] = sort([timeSeries.ReportRank]);
                timeSeries = timeSeries(timeSeriesOrder);

                %Create one section for each time series
                for n = 1:length(timeSeries)
                    plotIdx = arrayfun(@(x)any(timeSeries(n) == x.Parent),plotFigs);%timeSeries(n) == [plotFigs.Parent(1)];
                    plotsToAdd = plotFigs(plotIdx);
                    plotsToAdd = plotsToAdd(~[plotsToAdd.Reported]);
                    testIdx = arrayfun(@(x)any(timeSeries(n) == x.Parent),testFigs);
                    testsToAdd = testFigs(testIdx);
                    testsToAdd = testsToAdd(~[testsToAdd.Reported]);

                    tsSection = timeSeries(n).generateReportSection();
                    figToDelete = matlab.ui.Figure.empty();

                    if ~any(arrayfun(@(x)isa(x,'internal.EconModeler.EconComponents.Figures.TSPlot') && length(x.Parent) == 1 && x.Parent == timeSeries(n),plotsToAdd))
                       [plotSection,figGenerated] = internal.EconModeler.EconComponents.Figures.TSPlot.generateDefaultReportSection(timeSeries(n)); 
                       add(tsSection,plotSection);
                       figToDelete = [figToDelete;figGenerated]; %#ok<AGROW>
                    end

                    for m = 1:length(plotsToAdd)
                        [plotSection,figGenerated] = plotsToAdd(m).generateReportSection();
                        add(tsSection,plotSection);
                        figToDelete = [figToDelete;figGenerated]; %#ok<AGROW>
                        if ~isempty(testsToAdd) || m~=length(plotsToAdd) || ~isempty(e.Models) || n~=length(timeSeries)
                            add(tsSection,PageBreak());
                        end
                    end
                    for m = 1:length(testsToAdd)
                        if ~testsToAdd(m).hasTests()
                            continue;
                        end
                        testSection = testsToAdd(m).generateReportSection();
                        add(tsSection,testSection);
                        if m ~= length(testsToAdd) || ~isempty(e.Models) || n~=length(timeSeries)
                            add(tsSection,PageBreak());
                        end
                    end
                    add(report,tsSection);
                    reportedItems = [reportedItems;plotsToAdd;testsToAdd]; %#ok<AGROW>
                    close(figToDelete);
                end

                %Generate each requested model, and sort them by rank
                requestedModels = e.Models;
                models = internal.EconModeler.EconComponents.AbstractModel.empty(length(requestedModels),0);
                for n = 1:length(requestedModels)
                    models(n) = this.Session.getModel(requestedModels{n});
                end
                [~,modelOrder] = sort([models.ReportRank]);
                models = models(modelOrder);

                %Create one section for each model
                for n = 1:length(models)
                    plotIdx = arrayfun(@(x)any(models(n) == x.Parent),plotFigs);
                    plotsToAdd = plotFigs(plotIdx);
                    plotsToAdd = plotsToAdd(~[plotsToAdd.Reported]);
                    testIdx = arrayfun(@(x)any(models(n) == x.Parent),testFigs);
                    testsToAdd = testFigs(testIdx);
                    testsToAdd = testsToAdd(~[testsToAdd.Reported]);

                    modelSection = models(n).generateReportSection();
                    figToDelete = matlab.ui.Figure.empty;

                    if ~any(arrayfun(@(x)isa(x,'internal.EconModeler.EconComponents.Figures.ModelFitPlot') && x.Parent == models(n),plotsToAdd))
                        [plotSection,figGenerated] = internal.EconModeler.EconComponents.Figures.ModelFitPlot.generateDefaultReportSection(models(n));
                        add(modelSection,plotSection);
                        figToDelete = [figToDelete;figGenerated]; %#ok<AGROW>
                    end

                    for m = 1:length(plotsToAdd)
                        [plotSection,figGenerated] = plotsToAdd(m).generateReportSection();
                        add(modelSection,plotSection);
                        figToDelete = [figToDelete;figGenerated]; %#ok<AGROW>
                        if ~isempty(testsToAdd) || m~=length(plotsToAdd) || n~=length(models)
                            add(modelSection,PageBreak());
                        end
                    end
                    for m = 1:length(testsToAdd)
                        if ~testsToAdd(m).hasTests()
                            continue;
                        end
                        testSection = testsToAdd(m).generateReportSection();
                        add(modelSection,testSection);
                        if m ~= length(testsToAdd) || n~=length(models)
                            add(modelSection,PageBreak());
                        end
                    end
                    add(report,modelSection);
                    reportedItems = [reportedItems;plotsToAdd;testsToAdd]; %#ok<AGROW>
                    close(figToDelete);
                end    
                for r = 1:length(reportedItems)
                    reportedItems(r).Reported = false; %#ok<AGROW>
                end
                
                if this.Session.PrintReport
                    rptview(report);
                else
                    this.Session.LastReport = report;
                end
                
            catch ME
                %Set all item back to reported = false, so we can do report
                %generation again
                for r = 1:length(reportedItems)
                    reportedItems(r).Reported = false; %#ok<AGROW>
                end
                
                uialert(this.Session.App,ME.message,...
                    getString(message('econ:econModeler:AppTitle')),...
                    'Icon','error','Modal',true);
            end
        end
    end
end

