# ReportsManager.java

    /*
    ** Module   : ReportsManager.java
    ** Abstract : ReportsManager provides external API to generate reports.
    **
    ** Copyright (c) 2017-2023, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- ----------------Description----------------------
    ** 001 SBI 20170601 Created initial version.
    ** 002 TJD 20220504 Java 11 compatibility related minor changes
    ** 003 GBB 20230512 Logging methods replaced by CentralLogger/ConversionStatus.
    */
    /*
    ** This program is free software: you can redistribute it and/or modify
    ** it under the terms of the GNU Affero General Public License as
    ** published by the Free Software Foundation, either version 3 of the
    ** License, or (at your option) any later version.
    **
    ** This program is distributed in the hope that it will be useful,
    ** but WITHOUT ANY WARRANTY; without even the implied warranty of
    ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ** GNU Affero General Public License for more details.
    **
    ** You may find a copy of the GNU Affero GPL version 3 at the following
    ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
    **
    ** Additional terms under GNU Affero GPL version 3 section 7:
    **
    **   Under Section 7 of the GNU Affero GPL version 3, the following additional
    **   terms apply to the works covered under the License.  These additional terms
    **   are non-permissive additional terms allowed under Section 7 of the GNU
    **   Affero GPL version 3 and may not be removed by you.
    **
    **   0. Attribution Requirement.
    **
    **     You must preserve all legal notices or author attributions in the covered
    **     work or Appropriate Legal Notices displayed by works containing the covered
    **     work.  You may not remove from the covered work any author or developer
    **     credit already included within the covered work.
    **
    **   1. No License To Use Trademarks.
    **
    **     This license does not grant any license or rights to use the trademarks
    **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
    **     of Golden Code Development Corporation. You are not authorized to use the
    **     name Golden Code, FWD, or the names of any author or contributor, for
    **     publicity purposes without written authorization.
    **
    **   2. No Misrepresentation of Affiliation.
    **
    **     You may not represent yourself as Golden Code Development Corporation or FWD.
    **
    **     You may not represent yourself for publicity purposes as associated with
    **     Golden Code Development Corporation, FWD, or any author or contributor to
    **     the covered work, without written authorization.
    **
    **   3. No Misrepresentation of Source or Origin.
    **
    **     You may not represent the covered work as solely your work.  All modified
    **     versions of the covered work must be marked in a reasonable way to make it
    **     clear that the modified work is not originating from Golden Code Development
    **     Corporation or FWD.  All modified versions must contain the notices of
    **     attribution required in this license.
    */

    package com.goldencode.p2j.admin.server.reports;

    import java.io.*;
    import java.util.*;

    import com.goldencode.p2j.util.logging.*;
    import org.apache.pdfbox.pdmodel.*;
    import org.apache.pdfbox.pdmodel.common.*;
    import org.apache.pdfbox.pdmodel.font.*;

    import com.goldencode.p2j.admin.AdminServerImpl;
    import com.goldencode.p2j.admin.server.reports.PdfReportBuilder.*;
    import com.goldencode.p2j.admin.server.reports.ReportBuilder.*;
    import com.goldencode.p2j.admin.server.reports.TextReportBuilder.*;
    import com.goldencode.p2j.admin.shared.*;

    /**
     * Reports manager hides internal reports implementations. Provides external api to generate
     * reports.
     */
    public class ReportsManager
    {
       /** Logger */
       private static final CentralLogger LOG = CentralLogger.get(ReportsManager.class);

       /** The standard reports factory */
       private static ReportsFactory reportsFactory;

       /** The reports factory to build extended reports */
       private static ReportsFactory extReportsFactory;

       /** The extended reports factory class */
       private static Class<? extends ReportsFactory> extReportFactoryClass;

       /**
        * Gets the standard reports factory.
        *
        * @return   The standard reports factory
        */
       private static ReportsFactory getReportsFactory()
       {
          if (reportsFactory == null)
          {
             reportsFactory = new ReportsFactoryImpl();
          }

          return reportsFactory;
       }

       /**
        * Gets the extended reports factory.
        *
        * @return   The extended reports factory
        */
       private static ReportsFactory getExtReportsFactory()
       {
          if (extReportsFactory == null && extReportFactoryClass != null)
          {
             try
             {
                extReportsFactory = (ReportsFactory) extReportFactoryClass.getDeclaredConstructor().newInstance();
             }
             catch (ReflectiveOperationException e)
             {
                LOG.severe("Get ext reports factory exception", e);
             }
          }

          return extReportsFactory;
       }

       /**
        * Sets the report factory class to instantiate the extended report factory.
        *
        * @param    reportFactoryClass
        *           The report factory class
        */
       public static synchronized void setExtReportFactoryClass(Class<? extends ReportsFactory> reportFactoryClass)
       {
          ReportsManager.extReportFactoryClass = reportFactoryClass;
       }

       /**
        * Gets the requested report factory.
        *
        * @param    request
        *           The given report request
        *
        * @return   The requested report factory
        */
       public static synchronized ReportsFactory getReportsFactory(ReportRequest request)
       {
          String ext = request.getReportParameters().getOrDefault(
                   ReportParameters.EXTENSION.getParameter(), "");
          ReportsFactory factory;
          if (ext.isEmpty())
          {
             factory = getReportsFactory();
          }
          else
          {
             factory = getExtReportsFactory();
          }

          if (factory == null)
          {
             factory = getReportsFactory();
          }

          return factory;
       }

       /**
        * Builds the requested PDF report.
        *
        * @param    request
        *           The report request
        *
        * @return   The pdf document given by PDDocument
        *
        * @throws   IOException
        *           Throws this exception iff this IO operation is failed
        */
       public static PDDocument buildPDFReport(ReportRequest request)
       throws IOException
       {
          PdfDocument doc = buildPDFReportDocument(request);

          return doc.getDocument();
       }

       /**
        * Builds the requested PDF report.
        *
        * @param    request
        *           The report request
        *
        * @return   The pdf document holder
        *
        * @throws   IOException
        *           Throws this exception iff this IO operation is failed
        */
       private static PdfDocument buildPDFReportDocument(ReportRequest request)
       throws IOException
       {
          ReportsFactory factory = getReportsFactory(request);

          return factory.createPDFReportBuilder(request).buildReport(
                   buildReportParameters(request));
       }

       /**
        * Builds the requested PDF page report.
        *
        * @param    request
        *           The report request
        * @param    pageNumber
        *           The page number to preview its pdf report
        *
        * @return   The pdf document given by PDDocument
        *
        * @throws   IOException
        *           Throws this exception iff this IO operation is failed
        */
       public static PdfDocument buildPDFPagePreview(ReportRequest request, int pageNumber)
       throws IOException
       {
          request.getReportParameters().put(ReportParameters.PREVIEW_PAGE.getParameter(),
                   String.valueOf(pageNumber));

          return buildPDFReportDocument(request);
       }

       /**
        * Builds the requested report.
        *
        * @param    request
        *           The report request
        *
        * @return   The report document
        *
        * @throws   IOException
        *           Throws this exception iff this IO operation is failed
        */
       public static Document buildReport(ReportRequest request)
       throws IOException
       {
          ReportsFactory factory = getReportsFactory(request);

          String type = request.getReportParameters().getOrDefault(
                   ReportParameters.REPORT_TYPE.getParameter(), DocumentType.PDF.name());
          if (DocumentType.get(type) == DocumentType.PDF)
          {
             return factory.createPDFReportBuilder(request).buildReport(buildReportParameters(request));
          }
          else
          {
             return factory.createTextReportBuilder(request).buildReport(buildTextReportParameters(request));
          }
       }

       /**
        * Builds the requested report settings for the text report type.
        *
        * @param    request
        *           The text report request
        *
        * @return   The text report settings
        */
       public static TextReportSettings buildTextReportParameters(ReportRequest request)
       {
          return new TextReportSettings()
          {
          };
       }

       /**
        * Builds the requested report settings for the PDF report type.
        *
        * @param    request
        *           The PDF report request
        *
        * @return   The PDF report settings
        */
       public static PdfReportSettings buildReportParameters(ReportRequest request)
       {
          final Map<String, String> parameters = request.getReportParameters();

          final PaperOrientation orient = PaperOrientation.valueOf(
                   parameters.get(ReportParameters.PAPER_ORIENT.getParameter()));

          final PaperFormat format = PaperFormat.valueOf(
                   parameters.get(ReportParameters.PAPER_FORMAT.getParameter()));

          // these server report parameters can't be changed on the client side
          Map<String, Object> map = AdminServerImpl.getReportParametersMap();

          Integer maxPagesNumber = (Integer) map.get(ReportParameters.MAX_PAGES_NUMBER.getParameter());

          //report geometry settings
          float[] margin = new float[4];
          margin[0] = (Float) map.get(ReportParameters.LEFT_PAGE_MARGIN.getParameter());
          margin[1] = (Float) map.get(ReportParameters.TOP_PAGE_MARGIN.getParameter());
          margin[2] = (Float) map.get(ReportParameters.RIGHT_PAGE_MARGIN.getParameter());
          margin[3] = (Float) map.get(ReportParameters.BOTTOM_PAGE_MARGIN.getParameter());

          Float cellMargin = (Float) map.get(ReportParameters.CELL_MARGIN.getParameter());
          Float cellPadding = (Float) map.get(ReportParameters.CELL_PADDING.getParameter());
          Float headerMargin = (Float) map.get(ReportParameters.HEADER_MARGIN.getParameter());
          Float footerMargin = (Float) map.get(ReportParameters.FOOTER_MARGIN.getParameter());

          //report font settings
          Integer fontSize = (Integer) map.get(ReportParameters.FONT_SIZE.getParameter());
          Integer columnFontSize = (Integer) map.get(ReportParameters.COLUMN_FONT_SIZE.getParameter());
          Integer headerFontSize = (Integer) map.get(ReportParameters.HEADER_FONT_SIZE.getParameter());
          Integer footerFontSize = (Integer) map.get(ReportParameters.FOOTER_FONT_SIZE.getParameter());

          //TODO the font family selection
          //String fontName = (String) map.get(ReportParameters.FONT_NAME.getParameter());
          //String boldFontName = (String) map.get(ReportParameters.BOLD_FONT_NAME.getParameter());

          /**
           * The anonymous implementation of PDF report settings
           */
          return new PdfReportSettings() {

             /**
              * Gets the report rectangle.
              *
              * @return   The report rectangle
              */
             @Override
             public PDRectangle getReportSize()
             {
                switch(format)
                {
                   case LETTER:
                      return PDRectangle.LETTER;
                   case LEGAL:
                      return PDRectangle.LEGAL;
                   case A0:
                      return PDRectangle.A0;
                   case A1:
                      return PDRectangle.A1;
                   case A2:
                      return PDRectangle.A2;
                   case A3:
                      return PDRectangle.A3;
                   case A4:
                      return PDRectangle.A4;
                   case A5:
                      return PDRectangle.A5;
                   case A6:
                      return PDRectangle.A6;
                   default:
                      return PDRectangle.A4;
                }
             }

             /**
              * Gets the report paper orientation.
              *
              * @return   The paper orientation
              */
             @Override
             public PaperOrientation getPaperOrientation()
             {
                return orient;
             }

             /**
              * Gets the normal report font.
              *
              * @return   The normal report font
              */
             @Override
             public PDFont getFont()
             {
                return PDType1Font.COURIER;
             }

             /**
              * Gets the bold report font.
              *
              * @return   The bold report font
              */
             @Override
             public PDFont getBoldFont()
             {
                return PDType1Font.COURIER_BOLD;
             }

             /**
              * Gets the base font size.
              *
              * @return   The base font size
              */
             @Override
             public int getFontSize()
             {
                return fontSize.intValue();
             }

             /**
              * Gets the column header font size.
              *
              * @return   The column header font size
              */
             @Override
             public int getColumnFontSize()
             {
                return columnFontSize.intValue();
             }

             /**
              * Gets the cell font size.
              *
              * @return   The cell font size
              */
             @Override
             public int getCellFontSize()
             {
                return getFontSize();
             }

             /**
              * Gets the page header font size.
              *
              * @return   The page header font size
              */
             @Override
             public int getHeaderFontSize()
             {
                return headerFontSize.intValue();
             }

             /**
              * Gets the page footer font size.
              *
              * @return   The page footer font size
              */
             @Override
             public int getFooterFontSize()
             {
                return footerFontSize.intValue();
             }

             /**
              * Gets the page margin in this order: left, top, right and bottom. The values are
              * measured by points relative to the user space.
              *
              * @return   The page margin
              */
             @Override
             public float[] getPageMargin()
             {
                return margin;
             }

             /**
              * Gets the header margin measured by points relative to the user space.
              *
              * @return   The header margin
              */
             @Override
             public float getHeaderMargin()
             {
                return headerMargin.floatValue();
             }

             /**
              * Gets the footer margin measured by points relative to the user space.
              *
              * @return   The footer margin
              */
             @Override
             public float getFooterMargin()
             {
                return footerMargin.floatValue();
             }

             /**
              * Gets the cell margin measured by points relative to the user space.
              *
              * @return   The cell margin
              */
             @Override
             public float getCellMargin()
             {
                return cellMargin.floatValue();
             }

             /**
              * Gets the cell padding measured by points relative to the user space.
              *
              * @return   The cell padding
              */
             @Override
             public float getCellPadding()
             {
                return cellPadding.floatValue();
             }

             /**
              * Gets the maximal pages number.
              *
              * @return   The maximal pages number
              */
             @Override
             public int getMaxPagesNumber()
             {
                return maxPagesNumber;
             }
          };
       }
    }