package org.freehep.application;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.print.PageFormat;
import java.awt.print.Pageable;
import java.awt.print.Paper;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.util.Properties;

import javax.print.PrintService;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.standard.JobName;
import javax.print.attribute.standard.Media;
import javax.print.attribute.standard.MediaPrintableArea;
import javax.print.attribute.standard.MediaSize;
import javax.print.attribute.standard.MediaSizeName;

import org.freehep.application.studio.Studio;


/**
 *
 * @author tonyj
 */
public class PrintHelper implements Printable
{
   private static final String rootName = PrintHelper.class.getName() + ".";
   public final static int ORIENTATION_BEST_FIT = 0;
   public final static int ORIENTATION_PORTRAIT = 1;
   public final static int ORIENTATION_LANDSCAPE = 2;
   private Component target;
   private Font headerFooterFont = new Font("SansSerif", Font.PLAIN, 10);
   private PrintRequestAttributeSet atts = new HashPrintRequestAttributeSet();
   private PrinterJob pj;
   private String footer;
   private String header;
   private Studio app;
   private boolean drawBorder;
   private boolean scaleUp;
   private boolean showFooter;
   private boolean showHeader;
   private int orientation;

   public PrintHelper(Component target, Studio app)
   {
      this.target = target;
      this.app = app;

      Properties user = app.getUserProperties();

      drawBorder = PropertyUtilities.getBoolean(user, rootName + "drawBorder", false);
      scaleUp = PropertyUtilities.getBoolean(user, rootName + "scaleUp", false);
      showHeader = PropertyUtilities.getBoolean(user, rootName + "showHeader", true);
      showFooter = PropertyUtilities.getBoolean(user, rootName + "showFooter", false);
      footer = PropertyUtilities.getString(user, rootName + "footer", null);
      header = PropertyUtilities.getString(user, rootName + "header", "JAS3&b&d");
      orientation = PropertyUtilities.getInteger(user, rootName + "orientation", ORIENTATION_BEST_FIT);

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

      String defaultPrinter = user.getProperty(rootName + "defaultPrinter");
      PrintService ps = pj.getPrintService();
		if (ps == null) throw new RuntimeException("No Print Service Found");
      if ((defaultPrinter != null) && !ps.getName().equals(defaultPrinter))
      {
         PrintService[] services = PrinterJob.lookupPrintServices();
         for (int i = 0; i < services.length; i++)
         {
            if (services[i].getName().equals(defaultPrinter))
            {
               try
               {
                  pj.setPrintService(services[i]);
                  ps = services[i];
                  break;
               }
               catch (PrinterException x)
               {
                  x.printStackTrace();
               }
            }
         }
      }

      atts.add(new JobName(user.getProperty(rootName + "printJobName", "JAS3 Plots"), null));

      Media media = (Media) ps.getDefaultAttributeValue(Media.class);

      float x = PropertyUtilities.getFloat(user, rootName + "printArea.top", 0);
      float y = PropertyUtilities.getFloat(user, rootName + "printArea.left", 0);
      float w = PropertyUtilities.getFloat(user, rootName + "printArea.right", 0);
      float h = PropertyUtilities.getFloat(user, rootName + "printArea.bottom", 0);
      if (x > 0)
         atts.add(new MediaPrintableArea(x, y, w, h, MediaPrintableArea.INCH));
   }

   public void setDrawBorder(boolean drawBorder)
   {
      this.drawBorder = drawBorder;
   }

   public boolean isDrawBorder()
   {
      return drawBorder;
   }

   public void setFooter(String footer)
   {
      this.footer = footer;
   }

   public String getFooter()
   {
      return footer;
   }

   public void setHeader(String header)
   {
      this.header = header;
   }

   public String getHeader()
   {
      return header;
   }

   public void setOrientation(int orientation)
   {
      this.orientation = orientation;
   }

   public int getOrientation()
   {
      return orientation;
   }

   public void setScaleUp(boolean scaleUp)
   {
      this.scaleUp = scaleUp;
   }

   public boolean getScaleUp()
   {
      return scaleUp;
   }

   public void setShowFooter(boolean showFooter)
   {
      this.showFooter = showFooter;
   }

   public boolean isShowFooter()
   {
      return showFooter;
   }

   public void setShowHeader(boolean showHeader)
   {
      this.showHeader = showHeader;
   }

   public boolean isShowHeader()
   {
      return showHeader;
   }

   public int print(Graphics g, PageFormat pf, int pi) throws PrinterException
   {
      if (pi >= 1)
         return Printable.NO_SUCH_PAGE;

      Graphics2D orig = (Graphics2D) g;
      Graphics2D g2 = (Graphics2D) orig.create();
      g2.translate(pf.getImageableX(), pf.getImageableY());

      Dimension size = target.getSize();

      double pageWidth = pf.getImageableWidth();
      double pageHeight = pf.getImageableHeight();

      double portraiteRatio = Math.min(pageWidth / size.width, pageHeight / size.height);
      double landscapeRatio = Math.min(pageWidth / size.height, pageHeight / size.width);

      if (!scaleUp)
      {
         portraiteRatio = Math.min(portraiteRatio, 1);
         landscapeRatio = Math.min(landscapeRatio, 1);
      }

      boolean useLandscape = (orientation == ORIENTATION_BEST_FIT) ? (landscapeRatio > portraiteRatio) : (orientation == ORIENTATION_LANDSCAPE);

      if (useLandscape)
      {
         g2.rotate(Math.PI / 2);
         g2.translate(0, -pageWidth);
         g2.scale(landscapeRatio, landscapeRatio);
      }
      else
      {
         g2.scale(portraiteRatio, portraiteRatio);
      }
      target.print(g2);
      g2.dispose();
      if (drawBorder)
      {
         orig.setColor(Color.BLACK);
         orig.draw(new Rectangle2D.Double(pf.getImageableX(), pf.getImageableY(), pageWidth, pageHeight));
      }
      return Printable.PAGE_EXISTS;
   }

   void pageDialog(Component parent)
   {
      pj.pageDialog(atts);
   }

   public void print() throws PrinterException
   {
      pj.print(atts);
      commitChanges();
   }

   void printDialog(Component parent)
   {
      pj.printDialog(atts);
   }

   public void printPreview(Component parent) throws PrinterException
   {
      PrintPreview pp = new PrintPreview()
      {
         protected boolean onPrint(Pageable document) throws PrinterException
         {
            PrintHelper.this.print();
            return true;
         }

         protected void onError(PrinterException x)
         {
            app.error(this, "Print Error", x);
         }
      };

      // We need to get a PageFormat object corresponding to the current attributes
      PageFormat pf = pj.defaultPage();
      Media media = (Media) atts.get(Media.class);
      if (media instanceof MediaSizeName)
      {
         MediaSize ms = MediaSize.getMediaSizeForName((MediaSizeName) media);
         MediaPrintableArea ma = (MediaPrintableArea) atts.get(MediaPrintableArea.class);

         //Orientation
         Paper paper = pf.getPaper();

         paper.setSize(72 * ms.getX(ms.INCH), 72 * ms.getY(ms.INCH));
         paper.setImageableArea(72 * ma.getX(ma.INCH), 72 * ma.getY(ma.INCH), 72 * ma.getWidth(ma.INCH), 72 * ma.getHeight(ma.INCH));
         pf.setPaper(paper);
      }
      pp.setPrintable(this, pf);
      app.showDialog(pp.createDialog(parent), "printPreview");
   }

   public void showOptionsDialog(Component parent)
   {
      PrintDialog pd = new PrintDialog(app, this);
      int rc = pd.showDialog(parent);
      if (rc == pd.OK_OPTION)
      {
         commitChanges();
      }
   }

   private void commitChanges()
   {
      Properties user = app.getUserProperties();
      PropertyUtilities.setBoolean(user, rootName + "drawBorder", drawBorder);
      PropertyUtilities.setBoolean(user, rootName + "scaleUp", scaleUp);
      PropertyUtilities.setBoolean(user, rootName + "showHeader", showHeader);
      PropertyUtilities.setBoolean(user, rootName + "showFooter", showFooter);
      PropertyUtilities.setString(user, rootName + "footer", footer);
      PropertyUtilities.setString(user, rootName + "header", header);
      PropertyUtilities.setInteger(user, rootName + "orientation", orientation);
      PropertyUtilities.setString(user, rootName + "defaultPrinter", pj.getPrintService().getName());

      JobName jobName = (JobName) atts.get(JobName.class);
      if (jobName != null)
         PropertyUtilities.setString(user, rootName + "printJobName", jobName.getName());

      MediaPrintableArea mp = (MediaPrintableArea) atts.get(MediaPrintableArea.class);
      if (mp != null)
      {
         PropertyUtilities.setFloat(user, rootName + "printArea.x", mp.getX(mp.INCH));
         PropertyUtilities.setFloat(user, rootName + "printArea.y", mp.getY(mp.INCH));
         PropertyUtilities.setFloat(user, rootName + "printArea.w", mp.getWidth(mp.INCH));
         PropertyUtilities.setFloat(user, rootName + "printArea.h", mp.getHeight(mp.INCH));
      }
      Media media = (Media) atts.get(Media.class);
      if (media instanceof MediaSizeName)
      {
         MediaSize ms = MediaSize.getMediaSizeForName((MediaSizeName) media);
         PropertyUtilities.setString(user, rootName + "mediaSize", media.getName());
      }

   }
}
