/*
 * $Id: PDFPrintPage.java,v 1.4 2009-01-26 05:09:54 tomoke Exp $
 *
 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
package ch.randelshofer.pdf;

import ch.randelshofer.gui.ProgressObserver;
import com.sun.pdfview.PDFFile;
import com.sun.pdfview.PDFPage;
import com.sun.pdfview.PDFRenderer;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.print.*;
import java.io.IOException;

/**
 * A class representing a print job for a particular PDFFile.  The class
 * maintains a status dialog as it prints, allowing the user to cancel
 * the print job.
 */
public class PDFPrintPage implements Printable {

    /** The PDFFile to be printed */
    private PDFFile file;
    /** The PrinterJob for this print job */
    private PrinterJob pjob;
    /** A dialog box indicating printing status, with cancel button */
    private ProgressObserver pd;
    /** Set this to true to print inside of the imageable bounds.
     * This ensures that the entire page is visible.
     */
    private boolean printInsideImageableBounds = false;

    /**
     * Create a new PDFPrintPage object for a particular PDFFile.
     * @param file the PDFFile to be printed.
     */
    public PDFPrintPage(PDFFile file) {
        this.file = file;
    }

    /**
     * An example method to print a file.
     *
     * @param   setupPaper          true to post a page setup dialog
     * @throws java.io.IOException
     */
    public void printFile(boolean setupPaper, ProgressObserver p) throws IOException {

        this.pd = p;
        try {
            pd.setMinimum(0);
            pd.setMaximum(file.getNumPages());
            pd.setIndeterminate(false);
            pd.setNote("Preparing Printjob...");
            pd.setDoCancel(new Runnable() {

                @Override
                public void run() {
                    doCancel();
                }
            });
            PDFPrintPage pages = this;//new PDFPrintPage(file);

            // Create Print Job.
            // We set the margins to 0, on the default 8.5 x 11 paper

            PrinterJob pjob = PrinterJob.getPrinterJob();
            PageFormat pfDefault = PrinterJob.getPrinterJob().defaultPage();
            Paper defaultPaper = new Paper();

            if (file.getNumPages() > 0) {
                PDFPage page = file.getPage(0);
                defaultPaper.setSize(page.getWidth(), page.getHeight());
            }
            defaultPaper.setImageableArea(0, 0, defaultPaper.getWidth(), defaultPaper.getHeight());
            pfDefault.setPaper(defaultPaper);
            if (setupPaper) {
                pfDefault = PrinterJob.getPrinterJob().pageDialog(pfDefault);
            }
            pjob.setJobName("PDF Document");
            if (pjob.printDialog()) {

                pd.setNote("Printing...");
                pd.setIndeterminate(true);
                // validate the page against the chosen printer to correct
                // paper settings and margins
                pfDefault = pjob.validatePage(pfDefault);
                Book book = new Book();
                /*
                for (int i = 0, n = file.getNumPages(); i < n; i++) {
                    PDFPage page = file.getPage(i);
                    Paper paper = new Paper();
                    PageFormat pf = new PageFormat();
                    paper.setSize(page.getWidth(), page.getHeight());
                   /* if (page.getRotation() == 90 || page.getRotation() == 270) {
                        pf.setOrientation(PageFormat.LANDSCAPE);
                    } else {
                        pf.setOrientation(PageFormat.PORTRAIT);
                    }* /
                    paper.setImageableArea(0, 0, paper.getWidth(), paper.getHeight());
                    pf.setPaper(paper);
                    System.out.println("PDFPrintPage " + i + " A width=" + paper.getWidth() + " " + paper.getHeight() + " PAGE:" + page.getWidth() + " " + page.getHeight());
                    book.append(pages, pf, 1);
                    System.out.println("PDFPrintPage " + i + " B width=" + paper.getWidth() + " " + paper.getHeight() + " PAGE:" + page.getWidth() + " " + page.getHeight());
                }*/

                book.append(pages, pfDefault, file.getNumPages());
                pjob.setPageable(book);

                try {
                    pjob.print();
                } catch (PrinterException exc) {
                    System.out.println(exc);
                }
            }
        } finally {
            hide();
        }
    }

    /**
     * Show the progress dialog for this print job
     * @param pjob the PrinterJob representing the print job
     */
    public void show(PrinterJob pjob) {
        // nothing to do
    }

    /**
     * Close the progress dialog.  Don't use this method to cancel
     * the print job; use {@link #doCancel doCancel} instead.
     */
    public void hide() {
        pd.close();
    }

    /**
     * Cancel the print job.  Disables the cancel button, as it might
     * take a while for the cancel to take effect.
     */
    public void doCancel() {
        pjob.cancel();
    }

    // from Printable interface:  prints a single page, given a Graphics
    // to draw into, the page format, and the page number.
    @Override
    public int print(Graphics g, PageFormat format, int index)
            throws PrinterException {
        try {
            int pagenum = index + 1;

            // don't bother if the page number is out of range.
            if ((pagenum >= 1) && (pagenum <= file.getNumPages())) {

                // update the page number in the progress dialog
                pd.setNote("Printing Page " + String.valueOf(pagenum) + "...");
                pd.setIndeterminate(false);
                pd.setProgress(pagenum);

                // fit the PDFPage into the printing area
                Graphics2D g2 = (Graphics2D) g;
                PDFPage page = file.getPage(pagenum);


                double px, py, pwidth, pheight;

                if (printInsideImageableBounds) {
                    pwidth = format.getImageableWidth();
                    pheight = format.getImageableHeight();
                    px = format.getImageableX();
                    py = format.getImageableY();
                } else {
                    pwidth = format.getWidth();
                    pheight = format.getHeight();
                    px = 0;
                    py = 0;
                }


                double aspect = page.getAspectRatio();

                // handle page orientation matching
                double paperaspect = pwidth / pheight;
                if (Math.signum(paperaspect - 1.0) != Math.signum(aspect - 1)) {
                    /* Don't alter format - it does not work
                     * switch (format.getOrientation()) {
                    case PageFormat.REVERSE_LANDSCAPE:
                    case PageFormat.LANDSCAPE:
                    format.setOrientation(PageFormat.PORTRAIT);
                    break;
                    case PageFormat.PORTRAIT:
                    format.setOrientation(PageFormat.LANDSCAPE);
                    break;
                    }*/
                    // rotate page
                    AffineTransform t = new AffineTransform();
                    t.rotate(-Math.PI / 2, 0, 0);
                    t.translate(-format.getHeight(), 0);
                    g2.transform(t);
                    // rotate bounds
                    paperaspect = pheight / pwidth;
                    {
                        double swap = pheight;
                        pheight = pwidth;
                        pwidth = swap;
                    }
                    {
                        double swap = px;
                        px = py;
                        py = swap;
                    }
                }

                Rectangle imgbounds;
                int width;
                int height;
                if (aspect > paperaspect) {
                    // paper is too tall / pdfpage is too wide
                    height = (int) (pwidth / aspect);
                    width = (int) pwidth;
                } else {
                    // paper is too wide / pdfpage is too tall
                    width = (int) (pheight * aspect);
                    height = (int) pheight;
                }
                imgbounds = new Rectangle((int) (px
                        + (pwidth - width) / 2),
                        (int) (py + (pheight - height) / 2),
                        width, height);

                // render the page
                PDFRenderer pgs =
                        new PDFRenderer(page, g2, imgbounds, null, null);
                try {
                    page.waitForFinish();
                    pgs.run();
                } catch (InterruptedException ie) {
                }
                return PAGE_EXISTS;
            } else {
                return NO_SUCH_PAGE;
            }
        } catch (Throwable t) {
            t.printStackTrace();
            return NO_SUCH_PAGE;
        }
    }
}
