/*
 * Licensed Materials - Property of Perforce Software, Inc. 
 * © Copyright Perforce Software, Inc. 2014, 2021 
 * © Copyright IBM Corp. 2009, 2014
 * © Copyright ILOG 1996, 2009
 * All Rights Reserved.
 *
 * Note to U.S. Government Users Restricted Rights:
 * The Software and Documentation were developed at private expense and
 * are "Commercial Items" as that term is defined at 48 CFR 2.101,
 * consisting of "Commercial Computer Software" and
 * "Commercial Computer Software Documentation", as such terms are
 * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
 * as applicable.
 */

package print;

import ilog.views.chart.print.IlvChartPrintableDocument;
import ilog.views.gantt.action.IlvAction;
import ilog.views.schedule.print.IlvResourceDataChartPrintingController;
import ilog.views.schedule.IlvResourceDataChart;
import ilog.views.util.print.IlvFOUtil;
import ilog.views.util.print.IlvPrintPreviewPanel;

import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import java.awt.Color;
import java.awt.Window;
import java.awt.event.KeyEvent;
import java.awt.event.ActionEvent;
import java.io.File;

/**
 * This class groups a number of <code>IlvAction</code>s that can be used to
 * print the contents of a Resource Data chart.
 */
public class ResourceDataPrintActions {
  /**
   * The print controller that controls the printing of the chart.
   */
  static IlvResourceDataChartPrintingController printController;

  public static void initPrintingController(IlvResourceDataChart chart) {
    if (printController == null) {
      printController = new IlvResourceDataChartPrintingController(chart);
      printController.setPreviewMode(IlvPrintPreviewPanel.CONTINUOUS_MODE);
      printController.setComponentOrientation(chart.getComponentOrientation());
    }
    // Refreshes the IlvChartPrintableDocument in order
    // to reflect the visible changes of the chart.
    IlvChartPrintableDocument doc = (IlvChartPrintableDocument) printController.getDocument();
    doc.setResolutionScaleFactor(1.f);
    doc.setResizeMode(IlvChartPrintableDocument.SCALE_TO_DIMENSION);
    doc.setRepeatTitle(false);
  }

  /**
   * An action that prints a Resource Data chart
   */
  public static class PrintAction extends IlvAction {

    // The Resource Data chart to be printed
    private IlvResourceDataChart chart;

    /**
     * Creates the action for the specified chart.
     * @param chart The Resource Data chart to be printed.
     */
    public PrintAction(IlvResourceDataChart chart) {
      super("Print...",
            null,
            KeyStroke.getKeyStroke(KeyEvent.VK_P, KeyEvent.CTRL_DOWN_MASK),
            "Print",
            "Prints the chart.");
      setIcon(ResourceDataPrintActions.class, "images/printer.gif");
      this.chart = chart;
    }

    /**
     * Returns the chart that this action operates on.
     * @return The chart to be printed.
     */
    protected IlvResourceDataChart getChart() {
      return chart;
    }

    /**
     * This function is called when the user wants to preview the printing.
     * @param event The event is simply ignored.
     */
    Override
    public void actionPerformed(ActionEvent event) {
      initPrintingController(chart);
      try {
        printController.print(true);
      } catch (java.awt.print.PrinterException e) {

      }
    }

  }

  /**
   * An action that brings up the print setup dialog box.
   */
  public static class PrintSetupAction  extends IlvAction {

    // The Resource Data chart to be printed.
    private IlvResourceDataChart chart;

    /**
     * Creates the action for the specified chart.
     * @param chart The Resource Data chart to be printed.
     */
    public PrintSetupAction(IlvResourceDataChart chart) {
      super("Page Setup...",
            null,
            KeyStroke.getKeyStroke(KeyEvent.VK_U, KeyEvent.CTRL_DOWN_MASK),
            "Page Setup",
            "Brings the Page Setup dialog box.");
      setIcon(ResourceDataPrintActions.class, "images/page-setup.gif");
      this.chart = chart;
    }

    /**
     * Returns the chart that this action operates on.
     * @return The chart to be printed.
     */
    protected IlvResourceDataChart getChart() {
      return chart;
    }

    /**
     * This function is called when the user wants to preview the printing.
     * @param event The event is simply ignored.
     */
    Override
    public void actionPerformed(ActionEvent event) {
      initPrintingController(chart);
      printController.setupDialog(JOptionPane.getFrameForComponent(chart),
                                  true,
                                  true);
    }
  }

  /**
   * An action that brings up the preview dialog box.
   */
  public static class PrintPreviewAction extends IlvAction {

    // The Resource Data chart to be printed.
    private IlvResourceDataChart chart;

    /**
     * Creates the action for the specified chart.
     * @param chart The Resource Data chart to be printed.
     */
    public PrintPreviewAction(IlvResourceDataChart chart) {
      super("Print Preview...",
            null,
            KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.CTRL_DOWN_MASK),
            "Print Preview",
            "Brings up the Print Preview box.");
      setIcon(ResourceDataPrintActions.class, "images/preview.gif");
      this.chart = chart;
    }

    /**
     * Returns the chart that this action operates on.
     * @return The chart to be printed.
     */
    protected IlvResourceDataChart getChart() {
      return chart;
    }

    /**
     * This function is called when the user wants to preview the printing.
     * @param event The event is simply ignored.
     */
    Override
    public void actionPerformed(ActionEvent event) {
      initPrintingController(chart);
      printController.printPreview(JOptionPane.getFrameForComponent(chart));
    }

  }

  /**
   * An action that brings up the PDF save dialog box.
   */
  public static class SavePDFAction
      extends IlvAction
  {
    // The resource data chart to be printed.
    private IlvResourceDataChart chart;

    // The main window. Used for positioning the file chooser.
    private Window ownerWindow;

    /**
     * The file chooser for saving.
     */
    private JFileChooser filechooser;

    /**
     * Creates the action for the specified chart.
     * @param chart The resource data chart to be printed.
     */
    public SavePDFAction(IlvResourceDataChart chart, Window ownerWindow) {
      super("Save As PDF...",
            null,
            KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.CTRL_DOWN_MASK),
            "Save As PDF",
            "Brings up the Save As PDF dialog.");
      setIcon(ResourceDataPrintActions.class, "images/pdf.gif");
      this.chart = chart;
      this.ownerWindow = ownerWindow;
    }

    /**
     * Returns the chart that this action operates on.
     * @return The chart to be printed.
     */
    protected IlvResourceDataChart getChart() {
      return chart;
    }

    /**
     * This function is called when the user wants to save the chart as PDF.
     * @param event The event is simply ignored.
     */
    Override
    public void actionPerformed(ActionEvent event) {
      saveAsPDF();
    }

    /**
     * Implements the "Save as PDF" action.
     */
    protected void saveAsPDF()
    {
      // Let the user choose the destination file.
      if (filechooser == null) {
        filechooser = new JFileChooser();
        filechooser.setDialogTitle("Save as PDF");
        filechooser.setDialogType(JFileChooser.SAVE_DIALOG);
        filechooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
        filechooser.setFileFilter(
            createFileFilter(new String[] { "pdf", "PDF" },
            "Adobe PDF Files"));
      }
      File selectedFile = null;
      int returnVal = filechooser.showSaveDialog(ownerWindow);
      switch (returnVal) {
        case JFileChooser.CANCEL_OPTION:
          break;
        case JFileChooser.ERROR_OPTION:
          break;
        default:
          selectedFile = filechooser.getSelectedFile();
        break;
      }
      if (selectedFile == null)
        return;
      // Get a DOMImplementation
      DocumentBuilderFactory docBuilderFactory;
      try {
        docBuilderFactory = DocumentBuilderFactory.newInstance();
      } catch (FactoryConfigurationError e) {
        e.printStackTrace();
        return;
      }
      DocumentBuilder docBuilder;
      try {
        docBuilder = docBuilderFactory.newDocumentBuilder();
      } catch (ParserConfigurationException e) {
        e.printStackTrace();
        return;
      }
      DOMImplementation domImpl = docBuilder.getDOMImplementation();
      // Create an XSL-FO document.
      Document document = domImpl.createDocument(IlvFOUtil.FONamespaceUri, "fo:root", null);
      try {
  
        fillFODocument(document);
        // Save it as a file in PDF format.
        PDFGenerator.saveAsPDF(selectedFile, document);
  
      } catch (NoClassDefFoundError e) {
        if (e.getMessage().contains("org.apache.fop")
            || e.getMessage().contains("org/apache/fop")
            || e.getMessage().contains("org.apache.batik")
            || e.getMessage().contains("org/apache/batik")) {
          JOptionPane.showMessageDialog(
              null,
              "Apache FOP not found in the CLASSPATH: "+e.getMessage(),
              "Error during Saving",
              JOptionPane.ERROR_MESSAGE);
        } else {
          e.printStackTrace();
        }
      }
    }

    /**
     * Utility function: Creates a file filter for a JFileChooser.
     */
    private static FileFilter createFileFilter(final String[] extensions, final String extensionsDescription) {
      return
        new FileFilter() {
            Override
            public boolean accept (File f) {
              if (f.isDirectory())
                return true;
              String last = f.getName();
              int lastDot = last.lastIndexOf('.');
              if (lastDot >= 0) {
                String ext = last.substring(lastDot);
                for (int i = 0; i < extensions.length; i++)
                  if (extensions[i].equals(ext))
                    return true;
              }
              return false;
            }
            Override
            public String getDescription () {
              return extensionsDescription;
            }
        };
    }

    /**
     * Creates the complete XSL-FO document to be shown in the PDF.
     * The empty document is passed as argument; this methods fills in
     * its XSL-FO elements.
     * Subclasses should override this method as needed.
     */
    protected void fillFODocument(Document document)
    {
      PDFGenerator pdfgen = new PDFGenerator();
      Element chartElement = createResourceDataFOElement(document, pdfgen, true);
      // TODO: Use landscape.
      pdfgen.fillSinglePageDocument(document, chartElement);
    }

    /**
     * Creates the XSL-FO element to be shown in the PDF.
     */
    protected Element createResourceDataFOElement(Document document, PDFGenerator pdfgen, boolean scale)
    {
      // Add the chart to it, as a single page.
      synchronized(chart) {
        // Set the background to white, as usual for paper output.
        boolean oldOpaque = chart.isOpaque();
        Color oldBackground = chart.getBackground();
        chart.setOpaque(true);
        chart.setBackground(Color.white);
        try {
          if (scale) {
            // Scale the component so that it fits the available area.
            return IlvFOUtil.paintToFOFitted(document,
                                             new IlvFOUtil.ComponentPaintable(chart),
                                             (int) pdfgen.getAvailableArea().getWidth(),
                                             (int) pdfgen.getAvailableArea().getHeight(),
                                             SwingConstants.CENTER,
                                             SwingConstants.TOP);
          } else {
            // Use the chart component at its original size.
            return IlvFOUtil.paintToFOUnscaled(document,
                                               new IlvFOUtil.ComponentPaintable(chart));
          }
        } finally {
          chart.setOpaque(oldOpaque);
          chart.setBackground(oldBackground);
        }
      }
    }
  }
}