/*
 * Open Source Physics software is free software as described near the bottom of this code file.
 *
 * For additional information and documentation on Open Source Physics please see:
 * <http://www.opensourcephysics.org/>
 */

package org.opensourcephysics.display;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.print.Doc;
import javax.print.DocFlavor;
import javax.print.DocPrintJob;
import javax.print.PrintException;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.print.ServiceUI;
import javax.print.SimpleDoc;
import javax.print.StreamPrintService;
import javax.print.StreamPrintServiceFactory;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.standard.Chromaticity;
import javax.print.attribute.standard.OrientationRequested;
import javax.print.event.PrintJobAdapter;
import javax.print.event.PrintJobEvent;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;

/**
 * Print utilities for OSP componets.
 *
 * @author Wolfgang Christian
 * @version 1.0
 */
public class PrintUtils {
  private PrintUtils() {}

  // This method is like print() above but prints to a PostScript file
  // instead of printing to a printer.
  public static void saveComponentAsEPS(Component c) throws IOException {
    // Find a factory object for printing Printable objects to PostScript.
    DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
    String format = "application/postscript"; //$NON-NLS-1$
    StreamPrintServiceFactory factory = StreamPrintServiceFactory.lookupStreamPrintServiceFactories(flavor, format)[0];
    // Ask the user to select a file and open the selected file
    //JFileChooser chooser = new JFileChooser();
    JFileChooser chooser = OSPRuntime.getChooser();
    if(chooser.showSaveDialog(c)!=JFileChooser.APPROVE_OPTION) {
      return;
    }
    File f = chooser.getSelectedFile();
    FileOutputStream out = new FileOutputStream(f);
    // Obtain a PrintService that prints to that file
    StreamPrintService service = factory.getPrintService(out);
    // Do the printing with the method below
    printToService(c, service, null);
    // And close the output file.
    out.close();
  }

  public static void printComponent(Component c) {
    // Get a list of all printers that can handle Printable objects.
    DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
    PrintService[] services = PrintServiceLookup.lookupPrintServices(flavor, null);
    // Set some define printing attributes
    PrintRequestAttributeSet printAttributes = new HashPrintRequestAttributeSet();
    //printAttributes.add(OrientationRequested.LANDSCAPE); // landscape mode
    printAttributes.add(OrientationRequested.PORTRAIT);                    // PORTRAIT mode
    printAttributes.add(Chromaticity.MONOCHROME);                          // print in mono
    printAttributes.add(javax.print.attribute.standard.PrintQuality.HIGH); // highest resolution
    // Display a dialog that allows the user to select one of the
    // available printers and to edit the default attributes
    PrintService service = ServiceUI.printDialog(null, 100, 100, services, null, null, printAttributes);
    // If the user canceled, don't do anything
    if(service==null) {
      return;
    }
    // Now call a method defined below to finish the printing
    printToService(c, service, printAttributes);
  }

  public static void printToService(final Component c, PrintService service, PrintRequestAttributeSet printAttributes) {
    Printable printable = new PrintableComponent(c);
    DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
    Doc doc = new SimpleDoc(printable, flavor, null);
    DocPrintJob job = service.createPrintJob();
    final JOptionPane pane = new JOptionPane(DisplayRes.getString("PrintUtils.Printing.Message"), JOptionPane.PLAIN_MESSAGE); //$NON-NLS-1$
    JDialog dialog = pane.createDialog(c, DisplayRes.getString("PrintUtils.PrintDialog.Title")); //$NON-NLS-1$
    // This listener object updates the dialog as the status changes
    job.addPrintJobListener(new PrintJobAdapter() {
      public void printJobCompleted(PrintJobEvent e) {
        pane.setMessage(DisplayRes.getString("PrintUtils.PrintComplete.Message")); //$NON-NLS-1$
      }
      public void printDataTransferCompleted(PrintJobEvent e) {
        pane.setMessage(DisplayRes.getString("PrintUtils.PrintTransferred.Message")); //$NON-NLS-1$
      }
      public void printJobRequiresAttention(PrintJobEvent e) {
        pane.setMessage(DisplayRes.getString("PrintUtils.OutOfPaper.Message")); //$NON-NLS-1$
      }
      public void printJobFailed(PrintJobEvent e) {
        pane.setMessage(DisplayRes.getString("PrintUtils.PrintFailed.Message")); //$NON-NLS-1$
      }

    });
    // Show the dialog, non-modal.
    dialog.setModal(false);
    dialog.setVisible(true);
    // Now print the Doc to the DocPrintJob
    try {
      job.print(doc, printAttributes);
    } catch(PrintException e) {
      // Display any errors to the dialog box
      pane.setMessage(e.toString());
    }
  }

  /*
   * PrintableComponent copied from Java Examples in a Nutshell.
   * Copyright (c) 2004 David Flanagan.  All rights reserved.
   * This code is from the book Java Examples in a Nutshell, 3nd Edition.
   * It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
   * You may study, use, and modify it for any non-commercial purpose,
   * including teaching and use in open-source projects.
   * You may distribute it non-commercially as long as you retain this notice.
   * For a commercial use license, or to purchase the book,
   * please visit http://www.davidflanagan.com/javaexamples3.
*/
  public static class PrintableComponent implements Printable {
    Component c;

    /**
     * Constructor PrintableComponent
     * @param c
     */
    public PrintableComponent(Component c) {
      this.c = c;
    }

    // This method should print the specified page number to the specified
    // Graphics object, abiding by the specified page format.
    // The printing system will call this method repeatedly to print all
    // pages of the print job.  If pagenum is greater than the last page,
    // it should return NO_SUCH_PAGE to indicate that it is done.  The
    // printing system may call this method multiple times per page.
    public int print(Graphics g, PageFormat format, int pagenum) {
      // This implemenation is always a single page
      if(pagenum>0) {
        return Printable.NO_SUCH_PAGE;
      }
      // The Java 1.2 printing API passes us a Graphics object, but we
      // can always cast it to a Graphics2D object
      Graphics2D g2 = (Graphics2D) g;
      // Translate to accomodate the requested top and left margins.
      g2.translate(format.getImageableX(), format.getImageableY());
      // Figure out how big the drawing is, and how big the page
      // (excluding margins) is
      Dimension size = c.getSize();                    // component size
      double pageWidth = format.getImageableWidth();   // Page width
      double pageHeight = format.getImageableHeight(); // Page height
      // If the component is too wide or tall for the page, scale it down
      if(size.width>pageWidth) {
        double factor = pageWidth/size.width; // How much to scale
        g2.scale(factor, factor);             // Adjust coordinate system
        pageWidth /= factor;                  // Adjust page size up
        pageHeight /= factor;
      }
      if(size.height>pageHeight) { // Do the same thing for height
        double factor = pageHeight/size.height;
        g2.scale(factor, factor);
        pageWidth /= factor;
        pageHeight /= factor;
      }
      // Now we know the component will fit on the page.  Center it by
      // translating as necessary.
      g2.translate((pageWidth-size.width)/2, (pageHeight-size.height)/2);
      // Draw a line around the outside of the drawing area and label it
      g2.drawRect(-1, -1, size.width+2, size.height+2);
      // Set a clipping region so the component can't draw outside of
      // its won bounds.
      g2.setClip(0, 0, size.width, size.height);
      // Finally, print the component by calling its paint() method.
      // This prints the background, border, and children as well.
      // For swing components, if you don't want the background, border,
      // and children, then call printComponent() instead.
      c.paint(g);
      // Tell the PrinterJob that the page number was valid
      return Printable.PAGE_EXISTS;
    }

  }

}

/*
 * Open Source Physics software is free software; you can redistribute
 * it and/or modify it under the terms of the GNU General Public License (GPL) as
 * published by the Free Software Foundation; either version 2 of the License,
 * or(at your option) any later version.

 * Code that uses any portion of the code in the org.opensourcephysics package
 * or any subpackage (subdirectory) of this package must must also be be released
 * under the GNU GPL license.
 *
 * This software 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA
 * or view the license online at http://www.gnu.org/copyleft/gpl.html
 *
 * Copyright (c) 2007  The Open Source Physics project
 *                     http://www.opensourcephysics.org
 */
