/**
 *  Licensed under GPL. For more information, see
 *    http://jaxodraw.sourceforge.net/license.html
 *  or the LICENSE file in the jaxodraw distribution.
 */
package net.sf.jaxodraw.io;

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

import java.util.Locale;

import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.standard.Copies;
import javax.print.attribute.standard.JobName;
import javax.print.attribute.standard.OrientationRequested;

import javax.swing.JOptionPane;

import net.sf.jaxodraw.graph.JaxoGraph;
import net.sf.jaxodraw.gui.JaxoDialogs;
import net.sf.jaxodraw.util.JaxoDictionary;
import net.sf.jaxodraw.util.JaxoLog;
import net.sf.jaxodraw.util.graphics.JaxoGraphics2D;


/** Responsible for printing directly to a printer.
 * @since 2.0
 */
public class JaxoPrint implements Printable {
    private final JaxoDictionary language = new JaxoDictionary(JaxoPrint.class);
    private JaxoGraph jaxoGraph;

    /**
     * Constructor.
     *
     * @param graph the graph to print. Not null.
     */
    public JaxoPrint(final JaxoGraph graph) {
        this.jaxoGraph = graph;
    }

    /**
     * Constructor. Prepares to print an empty graph.
     */
    public JaxoPrint() {
        this.jaxoGraph = new JaxoGraph();
    }

    /** The actual printing routine.
     * @param g The specified graphics context.
     * @param pf The specified page format.
     * @param pageIndex The specified page index.
     * @return Either Printable.PAGE_EXISTS or Printable.NO_SUCH_PAGE.
     */
    public final int print(final Graphics g, final PageFormat pf, final int pageIndex) {
        final double transX = pf.getImageableX();
        final double transY = pf.getImageableY();

        Rectangle graphBB = jaxoGraph.getBounds();

        if (graphBB == null) {
            graphBB = new Rectangle();
        }

        final double scaleX = pf.getImageableWidth() / graphBB.getWidth();
        final double scaleY = pf.getImageableHeight() / graphBB.getHeight();

        if (pageIndex == 0) {
            final JaxoGraphics2D g2 = new JaxoGraphics2D((Graphics2D) g);

            g2.setPrinting(true);

            final double right = transX + pf.getImageableWidth() - graphBB.getX() - graphBB.getWidth();
            final double bottom = transY + pf.getImageableHeight() - graphBB.getY() - graphBB.getHeight();
            double scale = scaleX;

            if (scaleY < scaleX) {
                scale = scaleY;
            }

            if (scale > 1.) {
                scale = 1.d;
            }

            g2.translate(transX, transY);

            if (scale < 1.d) {
                // case 1: graph is bigger than printable area: scale it and shift to origin
                g2.scale(scale, scale);
                g2.translate(-graphBB.getX(), -graphBB.getY());
            } else {
                // case 2: graph size fits printable area but lies (partly) outside: just shift it back in
                if (right < 0.d) {
                    g2.translate(right - transX, 0.d);
                }

                if (bottom < 0.d) {
                    g2.translate(0.d, bottom - transY);
                }
            }

            jaxoGraph.paint(g2);

            return Printable.PAGE_EXISTS;
        } else {
            return Printable.NO_SUCH_PAGE;
        }
    }

    private void showPrintError(final Component parent, final String text) {
        JOptionPane.showMessageDialog(parent, text,
            JaxoDialogs.windowTitle(language, "errorTitle"),
            JOptionPane.ERROR_MESSAGE);
    }

    /** The method to be called from the printing menu.
     * @param parent parent Component for dialogs.
     * @param graph The JaxoGraph to be printed. Not null.
     * @param name Name for the job.
     * @since 2.0.2
     */
    public final void print(final Component parent, final JaxoGraph graph, final String name) {
        this.jaxoGraph = graph;
        print(parent, name);
    }

    /**
     * Print the current graph.
     *
     * @param parent parent Component for dialogs.
     * @param name Name for the job.
     * @since 2.1
     */
    public final void print(final Component parent, final String name) {
        final PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
        aset.add(OrientationRequested.LANDSCAPE);
        aset.add(new Copies(1));
        aset.add(new JobName(name, null));

        final PrinterJob pj = PrinterJob.getPrinterJob();
        pj.setPrintable(this);

        final PrintService[] services = PrinterJob.lookupPrintServices();

        final PrintService defaultService =
            PrintServiceLookup.lookupDefaultPrintService();

        final PrintService chosenService =
            (services.length > 0) ? services[0] : defaultService;

        if (chosenService == null) {
            showPrintError(parent, language.value("noPrinters"));
        } else {
            final Locale oldLocale = Locale.getDefault();

            try {
                pj.setPrintService(chosenService);

                Locale.setDefault(language.locale());

                if (pj.printDialog(aset)) {
                    pj.print(aset);
                }
            } catch (PrinterException e) {
                JaxoLog.debug(e);
                showPrintError(parent, language.message("print%0Failed", name));
            } finally {
                Locale.setDefault(oldLocale);
            }
        }
    }

    /** The method to be called from the printing menu.
     * @param parent parent Component for dialogs.
     * @param newGraph The JaxoGraph to be printed.
     * @param d The current dimension of the canvas. This is not used!
     * @param name Name for the job.
     * @deprecated use {@link #print(Component,JaxoGraph,String)} instead.
     */
    @Deprecated
    public final void print(final Component parent, final JaxoGraph newGraph, final Dimension d, final String name) {
        print(parent, newGraph, name);
    }
}
