/*******************************************************************************
 * This file is part of GECAMed.
 * 
 * GECAMed is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License (L-GPL) as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * GECAMed 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 (L-GPL)
 * along with GECAMed.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * GECAMed is Copyrighted by the Centre de Recherche Public Henri Tudor (http://www.tudor.lu)
 * (c) CRP Henri Tudor, Luxembourg, 2008
 *******************************************************************************/
package lu.tudor.santec.gecamed.core.gui.widgets;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.print.ServiceUI;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.Size2DSyntax;
import javax.print.attribute.standard.Copies;
import javax.print.attribute.standard.MediaPrintableArea;
import javax.print.attribute.standard.MediaSize;
import javax.print.attribute.standard.OrientationRequested;
import javax.print.attribute.standard.PageRanges;
import javax.print.attribute.standard.PrinterIsAcceptingJobs;
import javax.print.attribute.standard.QueuedJobCount;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JToggleButton;

import lu.tudor.santec.gecamed.core.gui.MainFrame;
import lu.tudor.santec.gecamed.usermanagement.gui.settings.UserSettingsPlugin;
import lu.tudor.santec.i18n.Relocalizable;
import lu.tudor.santec.i18n.Translatrix;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.export.JRPrintServiceExporter;
import net.sf.jasperreports.engine.export.JRPrintServiceExporterParameter;
import net.sf.jasperreports.engine.type.OrientationEnum;
import net.sf.jasperreports.view.JRSaveContributor;
import net.sf.jasperreports.view.JRViewer;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

/**
 * class for a general Jasper Print Preview Dialog
 *
 * @author Johannes Hermen johannes.hermen(at)tudor.lu
 *
 * @Version
 * <br>$Log: PrintPreviewDialog.java,v $
 * <br>Revision 1.34  2013-12-27 18:07:46  donak
 * <br>Cleanup of imports
 * <br>
 * <br>Revision 1.33  2013-07-15 06:18:34  ferring
 * <br>logging changed
 * <br>
 * <br>Revision 1.32  2011-08-19 07:20:53  ferring
 * <br>Reminder print dialog changed. Labels changed and notice joining added for payment order. But not yet activated, because it's not sure how to manage it in the report.
 * <br>
 * <br>Revision 1.31  2010-03-19 14:31:26  mack
 * <br>Added Default PageRanges attributes
 * <br>
 * <br>Revision 1.30  2009-11-25 09:54:23  mack
 * <br>Automatically presets page orientation depending on orientation of template
 * <br>
 * <br>Revision 1.29  2009-07-02 09:01:59  mack
 * <br>Made setting of printable area a bit nicer...
 * <br>
 * <br>Revision 1.28  2009-07-02 08:16:42  mack
 * <br>Explicitly sets print margins to 5 mm
 * <br>
 * <br>Revision 1.27  2009-07-02 08:14:57  mack
 * <br>Explicitly sets print margins to 5 mm
 * <br>
 * <br>Revision 1.26  2009-06-29 11:57:33  mack
 * <br>Fixed a bug causing printing of multiple copies to fail
 * <br>
 * <br>Revision 1.25  2009-03-31 08:34:08  hermen
 * <br>disabled stacktrace
 * <br>
 * <br>Revision 1.24  2009-03-23 13:16:54  heinemann
 * <br>fixed Ticket #264 Printing prescription blocks when hitting "Print" button in preview window
 * <br>
 * <br>printpreview dialog now owns a constructor that recieve a prent different from the mainframe.
 * <br>
 * <br>Revision 1.23  2009-03-16 14:17:27  hermen
 * <br>added patient incident statistics
 * <br>
 * <br>Revision 1.22  2009-03-10 09:51:42  hermen
 * <br>small bugfixes
 * <br>
 * <br>Revision 1.21  2008-09-25 09:43:06  heinemann
 * <br>fixed copyrights
 * <br>
 * <br>Revision 1.20  2008-01-15 09:29:38  hermen
 * <br>updated Javadoc and refactured code
 * <br>
 * <br>Revision 1.19  2007-12-06 14:46:44  hermen
 * <br>updated Javadoc and refactured code
 * <br>
 *
 */
public class PrintPreviewDialog extends JDialog implements ActionListener, Relocalizable{
	
	/**
	 * static logger for this class
	 */
	private static Logger logger = Logger.getLogger(PrintPreviewDialog.class
			.getName());
	
	public static final int ACCEPTING_JOBS = 0;
	public static final int NOT_ACCEPTING_JOBS = 1;
		
	public static final int A4_WIDTH = 210;
	public static final int A4_HEIGHT = 297;
	
	private static final long serialVersionUID = 1L;
	private JButton m_PrintButton;
	private JButton m_PrintAndCloseButton;
	private JRViewer l_InvoiceViewer;
	private JasperPrint m_PrintData;
	private boolean m_WasPrinted;
	private int		m_NumberOfCopies;

	//	create the print service exporter so that we can print to a named printer
	private JRPrintServiceExporter m_printExporter = new JRPrintServiceExporter();

	private PrintService m_printService;

	private Exception m_Exception;


	/**
	 * creates a new instance of the printpreview dialog
	 */
	public PrintPreviewDialog() {
		/* ================================================== */
		this(Translatrix.getTranslationString("PrintPreviewDialog.Title"));
		/* ================================================== */
	}
	
	/**
	 * creates a new instance of the printpreview dialog
	 */
	public PrintPreviewDialog(JDialog parent) {
		/* ================================================== */
		this(Translatrix.getTranslationString("PrintPreviewDialog.Title"), parent);
		/* ================================================== */
	}
	
	
	/**
	 * creates a new instance of the printpreview dialog with the given title
	 * @param title the dialog title
	 */
	public PrintPreviewDialog(String title) {
		/* ================================================== */
		super(MainFrame.getInstance(), true);
		initDialog(title);
		/* ================================================== */
	}
	
	/**
	 * creates a new instance of the printpreview dialog with the given title
	 * @param title the dialog title
	 * @param parent the parent of the dialog
	 */
	public PrintPreviewDialog(String title, JDialog parent) {
		/* ================================================== */
		super(parent, true);
		initDialog(title);
		/* ================================================== */
	}
	
	/**
	 * creates a new instance of the printpreview dialog with the given title
	 * @param title the dialog title
	 * @param parent the parent of the dialog
	 */
	public PrintPreviewDialog(String title, JFrame parent) {
		/* ================================================== */
		super(parent, true);
		initDialog(title);
		/* ================================================== */
	}
	
	
	/**
	 * Inits the dialog. is calles by the constructors
	 * 
	 * @param title
	 */
	private void initDialog(String title) {
		/* ================================================== */
		setTitle(title);
		setLayout(new BorderLayout());

		m_PrintButton 		  = new JButton ();
		m_PrintButton.addActionListener (this);
		m_PrintButton.setPreferredSize(new Dimension(110,23));

		m_PrintAndCloseButton = new JButton ();
		m_PrintAndCloseButton.addActionListener (this);

		m_PrintAndCloseButton.setPreferredSize(new Dimension(160,23));

		this.relocalize();
		/* ================================================== */
	}
	
	
	private boolean printReport (JasperPrint p_PrintData, int p_NumberOfCopies, boolean p_ShowPrintDialog) throws JRException
		{
//		boolean l_WasPrinted = false;
		PrintRequestAttributeSet	l_Attributes;
		OrientationRequested		l_Orientation;
		float[]						l_MediaSize;
		
		if (p_NumberOfCopies <= 0)
			// without printing it was printed as often as desired
			return true;
		
		//	set the report to print
		m_printExporter.setParameter( JRPrintServiceExporterParameter.JASPER_PRINT, p_PrintData);
		// tell the exporter to print 1 copy on A4 paper
		l_Attributes = new HashPrintRequestAttributeSet();
		
		l_Attributes.add(new Copies(p_NumberOfCopies));
		l_Attributes.add(new PageRanges (1,Integer.MAX_VALUE));
		
		l_Attributes.add(MediaSize.ISO.A4.getMediaSizeName());
		l_MediaSize = MediaSize.ISO.A4.getSize(Size2DSyntax.MM);
		if ((l_MediaSize != null) && (l_MediaSize.length == 2)) 
			{
			l_MediaSize[0] = (l_MediaSize[0] != 0)?l_MediaSize[0]:A4_WIDTH;
			l_MediaSize[1] = (l_MediaSize[1] != 0)?l_MediaSize[1]:A4_HEIGHT;
			}
		else
			{
			l_MediaSize[0] = A4_WIDTH;
			l_MediaSize[1] = A4_HEIGHT;
			}
		l_Attributes.add(new MediaPrintableArea (5,5,(l_MediaSize[0] - 10), (l_MediaSize[1] - 10), MediaPrintableArea.MM));
			
		logger.info("Media is " + l_MediaSize[0] + "mm wide and " + l_MediaSize[1] + "mm long!");
		
		if (p_PrintData.getOrientationValue().equals(OrientationEnum.LANDSCAPE))
			{
//			case OrientationEnum.LANDSCAPE:
				
				logger.info("Page Orientation is LANDSCAPE!");
				
				l_Orientation = OrientationRequested.LANDSCAPE;
				l_Attributes.add(l_Orientation);
//				break;
			}
		else
			{
//			case OrientationEnum.PORTRAIT:	
				
				logger.info("Page Orientation is PORTRAIT!");

				l_Orientation = OrientationRequested.PORTRAIT;
				l_Attributes.add(l_Orientation);
//				break;
			}
				
		m_printExporter.setParameter( JRPrintServiceExporterParameter.PRINT_REQUEST_ATTRIBUTE_SET, l_Attributes);
		
		PrintService svc = PrintServiceLookup.lookupDefaultPrintService();
		if (svc != null)
			logger.info("Default Printer is: " + svc.getName());
		else {
			logger.info("No default Printer found!!! Check your system configuration.");
			ErrorDialog.showErrorDialog(MainFrame.getInstance(), "No default Printer found!!!", 
										"Check your system configuration.", 
										"PrintServiceLookup.lookupDefaultPrintService() returned null.");
			return false;
		}
		
		if (p_ShowPrintDialog) {
			try {
				PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
				StringBuffer sb = new StringBuffer();
				if (services != null) {
					for (int i = 0; i < services.length; i++) {
						sb.append("\t\t" +services[i].toString() + "\n");
					}
				}
				logger.info("Showing Printer Dialog: \n" +
						"\tPrintRequestAttributeSet=" + l_Attributes + "\n" +
						"\tPrintServices: \n" +
						sb.toString());
				m_printService = ServiceUI.printDialog(null, 100, 100, services, svc, null, l_Attributes);	
			} catch (Exception e) {
				logger.log(Level.WARN, "Error showing printer Dialog", e);
				ErrorDialog.showErrorDialog(
						MainFrame.getInstance(), 
						"Error showing printer Dialog", 
						"failed to show printer dialog, \nswitching to default printer....", 
						e);
				m_printService = svc;
			}
		} else {
			m_printService = svc;
		}
		
		if (m_printService == null)
			return false;

		m_printExporter.setParameter( JRPrintServiceExporterParameter.PRINT_SERVICE, m_printService);

		try {
		    m_printExporter.setParameter( JRPrintServiceExporterParameter.PRINT_REQUEST_ATTRIBUTE_SET, l_Attributes);
		} catch (Exception e) {
			logger.warn("Error setting PRINT_REQUEST_ATTRIBUTE_SET " + e.getMessage());
		}
		
		try {
		    m_printExporter.setParameter( JRPrintServiceExporterParameter.PRINT_SERVICE_ATTRIBUTE_SET, m_printService.getAttributes());
		} catch (Exception e) {
			logger.warn("Error setting PRINT_SERVICE_ATTRIBUTE_SET " + e.getMessage());
		}
		
		//	print it
		m_printExporter.exportReport();

		return true;
		}
		
	/**
	 *  prints the given jasperprint
	 * @param p_PrintData the jasperprint
	 * @return true if doc was printed
	 */
	public boolean doPrint(JasperPrint p_PrintData) {
		return doPrint(p_PrintData, false,1);
	}

	public boolean doPrint(JasperPrint p_PrintData, int p_NumberOfCopies) {
		return doPrint(p_PrintData, false,p_NumberOfCopies);
	}

	/**
	 * prints the given jasperprint
	 * @param p_PrintData the jasperprint
	 * @param p_PrintImmediately if yes, default printer is used is not shown
	 * @return true if doc was printed
	 */
	public boolean doPrint(JasperPrint p_PrintData, boolean p_PrintImmediately, int p_NumberOfCopies) {
		JPanel l_MainViewerPanel;
		int l_Height;
		int l_Width;

		if (p_PrintData == null) return false;
		
		try {

			this.m_WasPrinted = false;
			this.m_PrintData = p_PrintData;
			m_NumberOfCopies = p_NumberOfCopies;
			m_Exception      = null;
			
			if (p_PrintImmediately) 
				{
				m_WasPrinted = this.printReport(m_PrintData, p_NumberOfCopies, false);
				return m_WasPrinted;
			}


			if (!showPrintPreview()) 
				{
				m_WasPrinted = this.printReport(m_PrintData, p_NumberOfCopies, showPrinterDialog());
				} 
			else {
				try {
					remove(l_InvoiceViewer);
				} catch (Exception e) {
				}
				l_InvoiceViewer = new JRViewer(p_PrintData);

			
				final JRSaveContributor[] DEFAULT_CONTRIBUTORS =  l_InvoiceViewer.getSaveContributors();
                                for (int i = 0; i < DEFAULT_CONTRIBUTORS.length; i++) {
                                    try {
                                        JRSaveContributor saveContrib = DEFAULT_CONTRIBUTORS[i];
                                        if (! ( saveContrib instanceof net.sf.jasperreports.view.save.JRPdfSaveContributor))
                                            l_InvoiceViewer.removeSaveContributor(saveContrib);
                                    } catch (Exception e) {
                                    }
                                }

				
				
				l_MainViewerPanel = ((JPanel) l_InvoiceViewer.getComponent(0));

				l_MainViewerPanel.remove(1);
				l_MainViewerPanel.add(new JPanel());

				l_MainViewerPanel.add(m_PrintButton);

				l_MainViewerPanel.add(new JPanel());

				l_MainViewerPanel.add(m_PrintAndCloseButton);

				// evil bugfix for fitpage which did not work correct
				try {
					((JToggleButton) l_MainViewerPanel.getComponent(10))
							.setSelected(true);
				} catch (Exception e) {
				}

				add(l_InvoiceViewer, BorderLayout.CENTER);


				setLocation(10, 10);

//				l_ScreenSize = Toolkit.getDefaultToolkit().getScreenSize();
//				l_Height = l_ScreenSize.height - 30;
//				l_Width = (int) (l_Height / c_PageAspectRatio);
				
//				System.out.println(p_PrintData.getPageHeight());
//				System.out.println(p_PrintData.getPageWidth());
//				System.out.println(p_PrintData.getOrientation());
				l_Width = (int) (p_PrintData.getPageWidth() * 1.2);
				l_Height = (int) (p_PrintData.getPageHeight() * 1.1);
				
				setSize(l_Width, l_Height);
				l_InvoiceViewer.setFitPageZoomRatio();

				setModal(true);
				setVisible(true);

			}

		} catch (Exception p_Exception) {
			logger.log(Level.WARN, "printing failed", p_Exception);
			m_Exception = p_Exception;
		}

		return m_WasPrinted;
	}

	/* (non-Javadoc)
	 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
	 */
	public void actionPerformed(ActionEvent p_Event)
	{
	if (p_Event.getSource() == m_PrintButton)
		{
		try	{
 			m_WasPrinted = this.printReport(m_PrintData, m_NumberOfCopies, showPrinterDialog());
			}
		catch (Exception p_Exception)
			{
			ErrorDialog.showErrorDialog(MainFrame.getInstance(), p_Exception);
			}
		}
	else if (p_Event.getSource() == m_PrintAndCloseButton)
		{
		try	{
			m_WasPrinted = this.printReport(m_PrintData, m_NumberOfCopies, showPrinterDialog());
			setVisible(false);
			}
		catch (Exception p_Exception)
			{
			ErrorDialog.showErrorDialog(MainFrame.getInstance(), p_Exception);
			}
		}
	}

	/* (non-Javadoc)
	 * @see lu.tudor.santec.i18n.Relocalizable#relocalize()
	 */
	public void relocalize()
	{
	if (m_PrintButton != null)
		m_PrintButton.setText (Translatrix.getTranslationString("PrintPreviewDialog.PrintButton"));

	if (m_PrintAndCloseButton != null)
		m_PrintAndCloseButton.setText (Translatrix.getTranslationString("PrintPreviewDialog.PrintAndCloseButton"));
	}

	/**
	 * @return
	 */
	private boolean showPrintPreview() {
		return (Boolean)MainFrame.getInstance().userSettings.getValue(UserSettingsPlugin.PRINTER_PREVIEW_ENABLED);
	}

	/**
	 * @return
	 */
	private boolean showPrinterDialog() {
		return (Boolean)MainFrame.getInstance().userSettings.getValue(UserSettingsPlugin.PRINTER_DIALOG_ENABLED);
	}

//	public void attributeUpdate(PrintServiceAttributeEvent psae) {
//		
//		PrinterIsAcceptingJobs printerIsAcceptingJobs = (PrinterIsAcceptingJobs) psae.getAttributes().get(PrinterIsAcceptingJobs.class);
//		if (printerIsAcceptingJobs.equals(PrinterIsAcceptingJobs.ACCEPTING_JOBS)) {
//			m_Status = ACCEPTING_JOBS;
//		} else {
//			m_Status = NOT_ACCEPTING_JOBS;
//		}
//		QueuedJobCount queuedJobCount = (QueuedJobCount) psae.getAttributes().get(QueuedJobCount.class);
//		m_QueuedJobCount = queuedJobCount.getValue();
//		
//		StringBuffer sb = new StringBuffer("Printservice updated: " + psae.toString() + "\n");
//		Attribute[] attr = psae.getAttributes().toArray();
//		for (int i = 0; i < attr.length; i++) {
//			sb.append("\t" + attr[i].getCategory() + "] " +  attr[i].getName() + ": " + attr[i] + "\n");
//		}
//		logger.info(sb.toString());
//	}
	
	public int getStatus() {
		if (m_printService == null) {
			logger.info("Status: Accepting Jobs PrintService==null");
			return ACCEPTING_JOBS;
		}
		int l_Status = 0;
		PrinterIsAcceptingJobs printerIsAcceptingJobs = (PrinterIsAcceptingJobs) m_printService.getAttributes().get(PrinterIsAcceptingJobs.class);
		if (printerIsAcceptingJobs.equals(PrinterIsAcceptingJobs.ACCEPTING_JOBS)) {
			l_Status = ACCEPTING_JOBS;
		} else {
			l_Status = NOT_ACCEPTING_JOBS;
		}
		logger.info("Status: " + ACCEPTING_JOBS);
		return l_Status;
	}
	
	public int getQueuedJobCount() {
		if (m_printService == null) {
			logger.info("QueuedJobCount: " + 0 + " PrintService==null");
			return 0;
		}
		QueuedJobCount queuedJobCount = (QueuedJobCount) m_printService.getAttributes().get(QueuedJobCount.class);
		int l_QueuedJobCount = queuedJobCount.getValue();
		logger.info("QueuedJobCount: " + l_QueuedJobCount);
		return l_QueuedJobCount;
	}

	/**
	 * @return the m_Exception
	 */
	public Exception getException() {
		return m_Exception;
	}
	
	

}
