/*
 * DocumentPrinting.java (original classname: PrintMe)
 * (C) Jan Michael Soan (http://it.toolbox.com/wiki/index.php/How_to_print_in_Java)
 * (C) 2009 University of Waikato, Hamilton NewZealand
 */

package weka.gui;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;

import javax.swing.JTextPane;
import javax.swing.text.Document;
import javax.swing.text.View;

/**
 * DocumentPrinting is a class that lets you print documents on
 * the fly for free ;) Printing in JDK 1.2 - 1.5 is hard.
 * With this, you just simply call it in your application
 * and add your text component like JTextPane:
 * <pre>
 * new DocumentPrinting().print(YourJTextComponent);
 * </pre>
 * Reminder: Just make sure there is a text on your component ;P
 * <pre>
 * Author : Jan Michael Soan
 * WebSite: http://www.jmsoan.com
 * Date   : 04/17/2004 
 * Time   : 2:20 PM 
 * </pre>
 * 
 * Found on <a href="http://it.toolbox.com/wiki/index.php/How_to_print_in_Java" target="_blank">Toolbox</a>
 * (<a href="http://www.toolbox.com/TermsofUse.aspx" target="_blank">Terms of Use</a>).
 * 
 * @author Jan Michael Soan (<a href="http://it.toolbox.com/wiki/index.php/How_to_print_in_Java" target="_blank">original code</a>)
 * @author FracPete (fracpete at waikato dot ac dot nz)
 */
public class DocumentPrinting
  implements Printable {

  /** the current page. */
  protected int m_CurrentPage = -1;
  
  /** the JTextPane which content is to be printed. */
  protected JTextPane m_PrintPane; 
  
  /** the page end. */
  protected double m_PageEndY = 0;
  
  /** the page start. */
  protected double m_PageStartY = 0;
  
  /** whether to scale the width to fit. */
  protected boolean m_ScaleWidthToFit = true; 
  
  /** the pageformat. */
  protected PageFormat m_PageFormat;
  
  /** the printer job. */
  protected PrinterJob m_PrinterJob;

  /**
   * Initializes the printing.
   */
  public DocumentPrinting() {
    m_PrintPane  = new JTextPane();
    m_PageFormat = new PageFormat();
    m_PrinterJob = PrinterJob.getPrinterJob();
  }

  /**
   * Brings up the page dialog.
   */
  protected void pageDialog() {
    m_PageFormat = m_PrinterJob.pageDialog(m_PageFormat);
  }

  /**
   * Prints the page.
   * 
   * @param graphics	the graphics context
   * @param pageFormat	the format of the page
   * @param pageIndex	the page index
   * @return		either NO_SUCH_PAGE or PAGE_EXISTS
   * @see		Printable#NO_SUCH_PAGE
   * @see		Printable#PAGE_EXISTS
   */
  public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) {
    double scale = 1.0;
    Graphics2D graphics2D;
    View rootView;

    graphics2D = (Graphics2D) graphics;
    m_PrintPane.setSize((int) pageFormat.getImageableWidth(),Integer.MAX_VALUE);
    m_PrintPane.validate();

    rootView = m_PrintPane.getUI().getRootView(m_PrintPane);

    if ((m_ScaleWidthToFit) && (m_PrintPane.getMinimumSize().getWidth() > pageFormat.getImageableWidth())) {
      scale = pageFormat.getImageableWidth()/
      m_PrintPane.getMinimumSize().getWidth();
      graphics2D.scale(scale,scale);
    }

    graphics2D.setClip(
	(int) (pageFormat.getImageableX()/scale),
	(int) (pageFormat.getImageableY()/scale),
	(int) (pageFormat.getImageableWidth()/scale),
	(int) (pageFormat.getImageableHeight()/scale));

    if (pageIndex > m_CurrentPage) {
      m_CurrentPage = pageIndex;
      m_PageStartY += m_PageEndY;
      m_PageEndY = graphics2D.getClipBounds().getHeight();
    }

    graphics2D.translate(
	graphics2D.getClipBounds().getX(),
	graphics2D.getClipBounds().getY());
    Rectangle allocation = new Rectangle(
	0,
	(int) -m_PageStartY,
	(int) (m_PrintPane.getMinimumSize().getWidth()),
	(int) (m_PrintPane.getPreferredSize().getHeight()));

    if (printView(graphics2D,allocation,rootView)) {
      return Printable.PAGE_EXISTS;
    }
    else {
      m_PageStartY = 0;
      m_PageEndY = 0;
      m_CurrentPage = -1;
      return Printable.NO_SUCH_PAGE;
    }
  }

  /**
   * Prints the document in the JTextPane.
   * 
   * @param pane	the document to print
   */
  public void print(JTextPane pane) {
    setDocument(pane);
    printDialog();
  }

  /**
   * Shows the print dialog.
   */
  public void printDialog() {
    if (m_PrinterJob.printDialog()) {
      m_PrinterJob.setPrintable(this,m_PageFormat);
      try {
	m_PrinterJob.print();
      }
      catch (PrinterException printerException) {
	m_PageStartY = 0;
	m_PageEndY = 0;
	m_CurrentPage = -1;
	System.out.println("Error Printing Document");
      }
    }
  }

  /**
   * Shows a print view.
   * 
   * @param graphics2D	the graphics context
   * @param allocation	the allocation
   * @param view	the view
   * @return		true if the page exists
   */
  protected boolean printView(Graphics2D graphics2D, Shape allocation, View view) {
    boolean pageExists = false;
    Rectangle clipRectangle = graphics2D.getClipBounds();
    Shape childAllocation;
    View childView;

    if (view.getViewCount() > 0) {
      for (int i = 0; i < view.getViewCount(); i++) {
	childAllocation = view.getChildAllocation(i,allocation);
	if (childAllocation != null) {
	  childView = view.getView(i);
	  if (printView(graphics2D,childAllocation,childView)) {
	    pageExists = true;
	  }
	}
      }
    } else {
      if (allocation.getBounds().getMaxY() >= clipRectangle.getY()) {
	pageExists = true;
	if ((allocation.getBounds().getHeight() > clipRectangle.getHeight()) &&
	    (allocation.intersects(clipRectangle))) {
	  view.paint(graphics2D,allocation);
	} else {
	  if (allocation.getBounds().getY() >= clipRectangle.getY()) {
	    if (allocation.getBounds().getMaxY() <= clipRectangle.getMaxY()) {
	      view.paint(graphics2D,allocation);
	    } else {
	      if (allocation.getBounds().getY() < m_PageEndY) {
		m_PageEndY = allocation.getBounds().getY();
	      }
	    }
	  }
	}
      }
    }
    return pageExists;
  }

  /**
   * Sets the content type.
   * 
   * @param type	the content type
   */
  public void setContentType(String type) {
    m_PrintPane.setContentType(type);
  }

  /**
   * Returns the document to print.
   * 
   * @return		the document or null
   */
  public Document getDocument() {
    if (m_PrintPane != null) 
      return m_PrintPane.getDocument();
    else 
      return null;
  }

  /**
   * Sets the document from the given JTextPane.
   * 
   * @param pane	the JTextPane to get the document from
   */
  public void setDocument(JTextPane pane) { 
    m_PrintPane = new JTextPane();
    setDocument(pane.getContentType(), pane.getDocument());
  }

  /**
   * Sets the document and the according content type.
   * 
   * @param type	the content type
   * @param document	the document to print
   */
  public void setDocument(String type, Document document) {
    setContentType(type);
    m_PrintPane.setDocument(document);
  }

  /**
   * Sets whether to scale the width to fit.
   * 
   * @param scaleWidth	if true then the width will be scaled
   */
  public void setScaleWidthToFit(boolean scaleWidth) {
    m_ScaleWidthToFit = scaleWidth;
  }

  /**
   * Returns whether the width is to be scaled.
   * 
   * @return		true if scaled
   */
  public boolean getScaleWidthToFit() {
    return m_ScaleWidthToFit;
  }
} 