/*******************************************************************************
 * 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.billing.gui.statement;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Currency;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.Timer;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import lu.tudor.santec.gecamed.billing.ejb.entity.beans.Invoice;
import lu.tudor.santec.gecamed.billing.ejb.entity.beans.InvoiceStub;
import lu.tudor.santec.gecamed.billing.ejb.entity.beans.Statement;
import lu.tudor.santec.gecamed.billing.ejb.session.interfaces.InvoiceInterface;
import lu.tudor.santec.gecamed.billing.gui.BillingModule;
import lu.tudor.santec.gecamed.billing.gui.invoice.stub.InvoiceStubUtils;
import lu.tudor.santec.gecamed.billing.gui.print.InvoicePrinter;
import lu.tudor.santec.gecamed.billing.utils.CurrencyFormat;
import lu.tudor.santec.gecamed.billing.utils.InvoiceWorkflow;
import lu.tudor.santec.gecamed.billing.utils.StatementWorkflow;
import lu.tudor.santec.gecamed.core.gui.GECAMedColors;
import lu.tudor.santec.gecamed.core.gui.GECAMedIconNames;
import lu.tudor.santec.gecamed.core.gui.GECAMedModule;
import lu.tudor.santec.gecamed.core.gui.MainFrame;
import lu.tudor.santec.gecamed.core.gui.utils.SwingWorker;
import lu.tudor.santec.gecamed.core.gui.widgets.ErrorDialog;
import lu.tudor.santec.gecamed.core.gui.widgets.animatedicon.AnimatedIcon;
import lu.tudor.santec.gecamed.core.gui.widgets.print.PrintParameterFetcher;
import lu.tudor.santec.gecamed.core.utils.PrintParameter;
import lu.tudor.santec.gecamed.usermanagement.gui.settings.UserSettingsPlugin;
import lu.tudor.santec.i18n.Relocalizable;
import lu.tudor.santec.i18n.Translatrix;

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

import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

//***************************************************************************
//* Class Definition and Members                                            *
//***************************************************************************

public class StatementPrintDialog extends JDialog implements ActionListener,
															 ItemListener,
															 ChangeListener,
															 Relocalizable 
{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	private CurrencyFormat				m_CurrencyFormat;
	private SimpleDateFormat			m_DateFormat;

	private JPanel						m_StatementPanel;
	private TitledBorder				m_StatementBorder;
	private JLabel						m_StatementReferenceLabel;
	private JLabel						m_StatementPeriodLabel;
	private JLabel						m_StatementPayerLabel;
	private JLabel						m_StatementInvoicesLabel;
	private JLabel						m_StatementAmountLabel;
	private JLabel						m_StatementReference;
	private JLabel						m_StatementFrom;
	private JLabel						m_StatementTo;
	private JLabel						m_StatementPayer;
	private JLabel						m_StatementInvoices;
	private JLabel						m_StatementAmount;
	private JPanel						m_PrintListPanel;
	private TitledBorder				m_PrintListBorder;
	private JLabel						m_PrintStepOne;
	private JButton						m_PrintListButton;
	
	private JPanel						m_PrintInvoicesPanel;
	private TitledBorder				m_PrintInvoicesBorder;
	private JLabel						m_PrintStepTwo;
	private JCheckBox					m_PrintAll;
	private JLabel						m_PrintFromLabel;
	private JSpinner					m_PrintFrom;
	private JLabel						m_PrintToLabel;
	private JSpinner					m_PrintTo;
	private JButton						m_PrintInvoicesButton;
	private JProgressBar				m_PrintInvoicesProgress;
	
	private ImageIcon					m_InvoicePrintIcon;
	private ImageIcon					m_SuspendPrintIcon;
	private AnimatedIcon				m_ResumePrintIcon;
	
	private JButton						m_CancelButton;
	
	private Statement					m_Statement;
	private Collection <InvoiceStub>	m_InvoiceStubs;
	
	private SwingWorker  				m_BatchPrinter;
	private Timer						m_Timer;
	private int							m_BatchSize;
	private boolean						m_Paused;
	private boolean						m_Suspend;
	private boolean						m_Aborted;
	private boolean						m_ListWasPrinted;
	private boolean						m_InvoicesWerePrinted;
	
	private InvoiceStubUtils			m_InvoiceStubUtils;
	
	private static final Collection <Integer> m_Dependencies = new ArrayList <Integer> ();
    
	static 	{
    		m_Dependencies.add(InvoiceInterface.c_PatientDependency);
 	    	}

	private static Logger m_Logger = Logger.getLogger (StatementPrintDialog.class.getName());

//***************************************************************************
//* Class Constants                                                         *
//***************************************************************************
	
	private final static Dimension c_PreferredSize = new Dimension (300,400);
	
	private final static int	   c_IconTextGap   = 10;
	
	private final static int	   c_TimerDelay	   = 5000;
	
	//=======================================================================
	//= Layout Strings
	//=======================================================================

	private final static String c_Columns				= "3dlu,fill:max(100dlu;pref):grow," +
														  "3dlu,fill:max(100dlu;pref):grow,3dlu";

	private final static String c_Rows					= "3dlu,fill:pref," +
	  													  "3dlu,fill:pref," +
	  													  "3dlu,fill:pref," +
														  "3dlu,fill:pref,3dlu";

	private final static String c_StatementColumns 		= "3dlu,fill:max(70dlu;pref)," +
														  "3dlu,fill:max(110dlu;pref),3dlu";

	private final static String c_StatementRows 		= "3dlu,fill:pref," +
	  													  "3dlu,fill:pref," +
	  													  "3dlu,fill:pref," +
	  													  "3dlu,fill:pref," +
														  "3dlu,fill:pref," +
														  "3dlu,fill:pref,3dlu";
 	
	private final static String c_PrintListColumns 		= "3dlu,fill:pref," +
	  													  "10dlu,fill:pref:grow,3dlu";

	private final static String c_PrintListRows 		= "3dlu,fill:pref,3dlu";

	private final static String c_PrintInvoicesColumns 	= "3dlu,fill:pref," +
	  													  "10dlu,fill:pref," +
	  													  "3dlu,fill:pref:grow," +
	  													  "3dlu,fill:pref," +
	  													  "3dlu,fill:pref:grow,3dlu";

	private final static String c_PrintInvoicesRows 	= "3dlu,fill:pref," +
	  													  "3dlu,fill:pref," +
	  													  "3dlu,fill:pref," +
	  													  "3dlu,fill:pref,3dlu";

	//=======================================================================
	//= I18N Strings
	//=======================================================================

	private final static String c_Title						= "StatementPrintDialog.Title";
	private final static String c_StatementBorder			= "StatementPrintDialog.StatementBorder";
	private final static String c_StatementReferenceLabel	= "StatementPrintDialog.StatementReferenceLabel";
	private final static String c_StatementPeriodLabel		= "StatementPrintDialog.StatementPeriodLabel";
	private final static String c_StatementFromLabel		= "StatementPrintDialog.StatementFromLabel";
	private final static String c_StatementToLabel			= "StatementPrintDialog.StatementToLabel";
	private final static String c_StatementPayerLabel		= "StatementPrintDialog.StatementPayerLabel";
	private final static String c_StatementInvoicesLabel	= "StatementPrintDialog.StatementInvoicesLabel";
	private final static String c_StatementAmountLabel		= "StatementPrintDialog.StatementAmountLabel";
	
	private final static String c_PrintListBorder			= "StatementPrintDialog.PrintListBorder";
	private final static String c_PrintListButton			= "StatementPrintDialog.PrintListButton";
	private final static String c_PrintInvoicesBorder		= "StatementPrintDialog.PrintInvoicesBorder";
	private final static String c_PrintInvoicesButton		= "StatementPrintDialog.PrintInvoicesButton";
	private final static String c_PrintAllLabel				= "StatementPrintDialog.PrintAllLabel";
	private final static String c_PrintFromLabel			= "StatementPrintDialog.PrintFromLabel";
	private final static String c_PrintToLabel				= "StatementPrintDialog.PrintToLabel";
	private final static String c_PrintSuspendButton		= "StatementPrintDialog.PrintSuspendButton";
	private final static String c_PrintResumeButton			= "StatementPrintDialog.PrintResumeButton";
	private final static String c_PrintProgressIdle			= "StatementPrintDialog.PrintProgressIdle";
	private final static String c_PrintProgressDone			= "StatementPrintDialog.PrintProgressDone";
	private final static String c_PrintProgressAbort		= "StatementPrintDialog.PrintProgressAbort";
	private final static String c_PrintProgressPaused		= "StatementPrintDialog.PrintProgressPaused";
	private final static String c_WaitingForPrinter			= "StatementPrintDialog.WaitingForPrinter";

	private final static String c_PrintErrorTitle			= "StatementPrintDialog.PrintErrorTitle";
	private final static String c_PrintErrorMessage			= "StatementPrintDialog.PrintErrorMessage";

	private final static String c_NoError					= "StatementPrintDialog.NoError"; 
	private final static String c_TooManyJobsInQueue		= "StatementPrintDialog.TooManyJobsInQueue"; 
	private final static String c_DidNotAcceptJobs			= "StatementPrintDialog.DidNotAcceptJobs"; 
	private final static String c_PrinterError				= "StatementPrintDialog.PrinterError"; 
	
//---------------------------------------------------------------------------
//***************************************************************************
//* Constructor                                                             *
//***************************************************************************
//---------------------------------------------------------------------------

public StatementPrintDialog ()
	{
	super (MainFrame.getInstance(),Translatrix.getTranslationString(c_Title),true);
	
	CellConstraints	l_Constraints;
	FormLayout		l_Layout;	
	
	m_ListWasPrinted 	  = false;
	m_InvoicesWerePrinted = false;
	
	this.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
	
	l_Constraints  = new CellConstraints();
	
	m_StatementBorder 		= new TitledBorder ("");
	m_PrintListBorder 		= new TitledBorder ("");
	m_PrintInvoicesBorder 	= new TitledBorder ("");
	
	m_StatementReferenceLabel 	= new JLabel ();
	m_StatementReferenceLabel.setForeground(GECAMedColors.c_LabelColor);
	
	m_StatementPeriodLabel 	= new JLabel ();
	m_StatementPeriodLabel.setForeground(GECAMedColors.c_LabelColor);
	
	m_StatementPayerLabel 		= new JLabel ();
	m_StatementPayerLabel.setForeground(GECAMedColors.c_LabelColor);
	
	m_StatementInvoicesLabel 	= new JLabel ();
	m_StatementInvoicesLabel.setForeground(GECAMedColors.c_LabelColor);
	
	m_StatementAmountLabel 	= new JLabel ();
	m_StatementAmountLabel.setForeground(GECAMedColors.c_LabelColor);
	
	m_PrintFromLabel		= new JLabel ();
	m_PrintFromLabel.setForeground(GECAMedColors.c_LabelColor);
	
	m_PrintToLabel		= new JLabel ();
	m_PrintToLabel.setForeground(GECAMedColors.c_LabelColor);
	
	m_CancelButton = new JButton ();
	m_CancelButton.setIcon(GECAMedModule.getSmallIcon(GECAMedIconNames.CANCEL));
	m_CancelButton.addActionListener(this);
	
	this.getContentPane().setBackground(GECAMedColors.c_GECAMedBackground);
	this.relocalize();
		
	//=======================================================================
	//= Assemble Statement Panel
	//=======================================================================
	
	l_Layout = new FormLayout(c_StatementColumns, c_StatementRows);
	m_StatementBorder = new TitledBorder (Translatrix.getTranslationString(c_PrintListBorder));
	
	m_StatementPanel = new JPanel ();
	m_StatementPanel.setOpaque(false);
	m_StatementPanel.setLayout(l_Layout);
	m_StatementPanel.setBorder(m_StatementBorder);
	
	m_StatementReference = new JLabel ("n/a");
	m_StatementFrom    	= new JLabel ("n/a");
	m_StatementTo    	= new JLabel ("n/a");
	m_StatementPayer     = new JLabel ("n/a");
	m_StatementInvoices  = new JLabel ("0");
	m_StatementAmount    = new JLabel (m_CurrencyFormat.format(0.0));

	m_StatementPanel.add (m_StatementReferenceLabel,	l_Constraints.xywh(2, 2, 1, 1));
	m_StatementPanel.add (m_StatementReference,   		l_Constraints.xywh(4, 2, 1, 1));
	m_StatementPanel.add (m_StatementPeriodLabel,		l_Constraints.xywh(2, 4, 1, 1));
	m_StatementPanel.add (m_StatementFrom,   			l_Constraints.xywh(4, 4, 1, 1));
	m_StatementPanel.add (m_StatementTo,   				l_Constraints.xywh(4, 6, 1, 1));
	m_StatementPanel.add (m_StatementPayerLabel,    	l_Constraints.xywh(2, 8, 1, 1));
	m_StatementPanel.add (m_StatementPayer,   			l_Constraints.xywh(4, 8, 1, 1));
	m_StatementPanel.add (m_StatementInvoicesLabel,  	l_Constraints.xywh(2,10, 1, 1));
	m_StatementPanel.add (m_StatementInvoices,   		l_Constraints.xywh(4,10, 1, 1));
	m_StatementPanel.add (m_StatementAmountLabel,    	l_Constraints.xywh(2,12, 1, 1));
	m_StatementPanel.add (m_StatementAmount,   			l_Constraints.xywh(4,12, 1, 1));
	
	//=======================================================================
	//= Assemble Print List Panel
	//=======================================================================
	
	l_Layout = new FormLayout(c_PrintListColumns, c_PrintListRows);
	m_PrintListBorder = new TitledBorder (Translatrix.getTranslationString(c_PrintListBorder));

	m_PrintListPanel = new JPanel ();
	m_PrintListPanel.setOpaque(false);
	m_PrintListPanel.setLayout(l_Layout);
	m_PrintListPanel.setBorder(m_PrintListBorder);
	
	m_PrintStepOne = new JLabel ();
	m_PrintStepOne.setIcon(BillingModule.getIconResource("step1.png"));
	m_PrintStepOne.setText(null);
	m_PrintStepOne.setToolTipText(null);
	
	m_PrintListButton = new JButton ();
	m_PrintListButton.setIcon(BillingModule.getIconResource("stmnt_print_list.png"));
	m_PrintListButton.setText(Translatrix.getTranslationString(c_PrintListButton));
	m_PrintListButton.setIconTextGap(c_IconTextGap);
	m_PrintListButton.addActionListener(this);
	
	m_PrintListPanel.add (m_PrintStepOne,    l_Constraints.xywh( 2, 2, 1, 1));
	m_PrintListPanel.add (m_PrintListButton, l_Constraints.xywh( 4, 2, 1, 1));

	//=======================================================================
	//= Assemble Print Invoices Panel
	//=======================================================================
	
	l_Layout = new FormLayout(c_PrintInvoicesColumns, c_PrintInvoicesRows);
	m_PrintInvoicesBorder = new TitledBorder (Translatrix.getTranslationString(c_PrintInvoicesBorder));

	m_PrintInvoicesPanel = new JPanel ();
	m_PrintInvoicesPanel.setOpaque(false);
	m_PrintInvoicesPanel.setLayout(l_Layout);
	m_PrintInvoicesPanel.setBorder(m_PrintInvoicesBorder);
	
	m_PrintStepTwo = new JLabel ();
	m_PrintStepTwo.setIcon(BillingModule.getIconResource("step2.png"));
	m_PrintStepTwo.setDisabledIcon(BillingModule.getIconResource("step2_disabled.png"));
	m_PrintStepTwo.setText(null);
	m_PrintStepTwo.setToolTipText(null);
	
	m_PrintAll = new JCheckBox (Translatrix.getTranslationString (c_PrintAllLabel));
	m_PrintAll.setOpaque(false);
    m_PrintAll.addItemListener(this);
	m_PrintAll.setSelected(true);
	
    m_PrintFrom   = new JSpinner (new SpinnerNumberModel ());
	m_PrintFrom.addChangeListener(this);
    m_PrintFrom.setEnabled(false);
	m_PrintFromLabel.setEnabled(false);
	
	m_PrintTo = new JSpinner (new SpinnerNumberModel ());
	m_PrintTo.addChangeListener(this);
	m_PrintTo.setEnabled(false);
	m_PrintToLabel.setEnabled(false);

    m_InvoicePrintIcon = BillingModule.getIconResource("stmnt_print_invoices.png");
    m_SuspendPrintIcon = BillingModule.getIconResource("stmnt_print_suspend.png");
    m_ResumePrintIcon  = new AnimatedIcon (BillingModule.getIconResource("stmnt_print_paused.gif"));
	
	m_PrintInvoicesButton = new JButton ();
	m_PrintInvoicesButton.setIcon(m_InvoicePrintIcon);
	m_PrintInvoicesButton.setText(Translatrix.getTranslationString(c_PrintInvoicesButton));
	m_PrintInvoicesButton.setIconTextGap(c_IconTextGap);
	m_PrintInvoicesButton.addActionListener(this);
	
	m_PrintInvoicesProgress = new JProgressBar ();
	m_PrintInvoicesProgress.setMinimum(0);
	m_PrintInvoicesProgress.setValue(0);
	m_PrintInvoicesProgress.setStringPainted(true);
	m_PrintInvoicesProgress.setString (Translatrix.getTranslationString(c_PrintProgressIdle));
	
	m_PrintInvoicesPanel.add (m_PrintStepTwo, 	l_Constraints.xywh( 2, 2, 1, 1));
	m_PrintInvoicesPanel.add (m_PrintAll,	  	l_Constraints.xywh( 4, 2, 7, 1));
	m_PrintInvoicesPanel.add (m_PrintFromLabel,	l_Constraints.xywh( 4, 4, 1, 1));
	m_PrintInvoicesPanel.add (m_PrintFrom,		l_Constraints.xywh( 6, 4, 1, 1));
	m_PrintInvoicesPanel.add (m_PrintToLabel,	l_Constraints.xywh( 8, 4, 1, 1));
	m_PrintInvoicesPanel.add (m_PrintTo,		l_Constraints.xywh( 10, 4, 1, 1));
	
	m_PrintInvoicesPanel.add (m_PrintInvoicesButton, l_Constraints.xywh( 4, 6, 7, 1));
	m_PrintInvoicesPanel.add (m_PrintInvoicesProgress, l_Constraints.xywh( 4, 8, 7, 1));

	this.setEnabled (m_PrintInvoicesPanel, false);

	//=======================================================================
	//= Assemble Main Panel
	//=======================================================================
	
	this.getContentPane().setBackground(GECAMedColors.c_GECAMedBackground);

	l_Layout = new FormLayout(c_Columns, c_Rows);
	this.setLayout (l_Layout);
	
	this.add (m_StatementPanel,  		l_Constraints.xywh(2, 2, 3, 1));
	this.add (m_PrintListPanel,  		l_Constraints.xywh(2, 4, 3, 1));
	this.add (m_PrintInvoicesPanel,  	l_Constraints.xywh(2, 6, 3, 1));
	this.add (m_CancelButton,  			l_Constraints.xywh(4, 8, 1, 1));
	
	this.setSize(c_PreferredSize);
	}

//---------------------------------------------------------------------------
//***************************************************************************
//* Interface Getters                                                       *
//***************************************************************************
//---------------------------------------------------------------------------

//private InvoiceInterface getInvoiceInterface ()
//	{
//	if (m_InvoiceInterface != null) return m_InvoiceInterface;
//
//	try {
//		m_InvoiceInterface = (InvoiceInterface) ManagerFactory.getRemote(InvoiceBean.class);
////		InitialContext l_Context = new InitialContext();
////		m_InvoiceInterface = (InvoiceInterface) l_Context.lookup("InvoiceBean/remote");
////		l_Context.close();
//		} 
//	catch (Exception p_Exception) 
//		{
//		m_Logger.warn(p_Exception.getLocalizedMessage());
//		}
//
//	return m_InvoiceInterface;
//	}

//---------------------------------------------------------------------------
//***************************************************************************
//* Class Primitives                                                        *
//***************************************************************************
//---------------------------------------------------------------------------

private void togglePrintButton (boolean p_SuspendButton)
	{
	if (p_SuspendButton)
		{
		m_PrintInvoicesButton.setIcon(m_SuspendPrintIcon);
		m_PrintInvoicesButton.setText(Translatrix.getTranslationString(c_PrintSuspendButton));	
		}
	else
		{
		m_PrintInvoicesButton.setIcon(m_InvoicePrintIcon);
		m_PrintInvoicesButton.setText(Translatrix.getTranslationString(c_PrintInvoicesButton));	
		}
	}

//---------------------------------------------------------------------------

private void toggleResumeButton (boolean p_ResumeButton)
	{
	if (p_ResumeButton)
		{
		m_PrintInvoicesButton.setIcon(m_ResumePrintIcon);
		m_PrintInvoicesButton.setText(Translatrix.getTranslationString(c_PrintResumeButton));	
		}
	else
		{
		m_PrintInvoicesButton.setIcon(m_SuspendPrintIcon);
		m_PrintInvoicesButton.setText(Translatrix.getTranslationString(c_PrintSuspendButton));	
		}
	}

//---------------------------------------------------------------------------

private void setEnabled (JPanel p_Panel, boolean p_EnableIt)
	{
	Component [] l_Children;
	int			 l_ChildIndex;
	
	if (p_Panel == null) return;
	
	l_Children = p_Panel.getComponents();
	for (l_ChildIndex = 0; l_ChildIndex < l_Children.length; l_ChildIndex++)
		{
		l_Children [l_ChildIndex].setEnabled(p_EnableIt);
		}
	}
	
//---------------------------------------------------------------------------

private void printInvoiceList ()
	{
	InvoicePrinter	l_Printer;
	
	if ((m_Statement != null) && (m_InvoiceStubs != null))
		{
		l_Printer = new InvoicePrinter ();
		m_ListWasPrinted = l_Printer.printStatementCover(m_Statement, m_InvoiceStubs, null);
		l_Printer.close();
		}
	}

//---------------------------------------------------------------------------

private int getPrintFrom ()
	{
	int	l_PrintFrom;
	int l_PrintTo;
	
	l_PrintFrom = ((Integer)m_PrintFrom.getValue()).intValue();
	l_PrintTo   = ((Integer)m_PrintTo.getValue()).intValue();
	
	return Math.min(l_PrintFrom, l_PrintTo);
	}

//---------------------------------------------------------------------------

private int getPrintTo ()
	{
	int	l_PrintFrom;
	int l_PrintTo;
	
	l_PrintFrom = ((Integer)m_PrintFrom.getValue()).intValue();
	l_PrintTo   = ((Integer)m_PrintTo.getValue()).intValue();
	
	return Math.max(l_PrintFrom, l_PrintTo);
	}

//---------------------------------------------------------------------------

private String getMeaningOf (int p_PrinterStatus)
	{
	switch (p_PrinterStatus)
		{
		case InvoicePrinter.c_NoError 			 : return Translatrix.getTranslationString(c_NoError);
		case InvoicePrinter.c_TooManyJobsInQueue : return Translatrix.getTranslationString(c_TooManyJobsInQueue);
		case InvoicePrinter.c_DidNotAcceptJobs	 : return Translatrix.getTranslationString(c_DidNotAcceptJobs);
		case InvoicePrinter.c_PrinterError		 : return Translatrix.getTranslationString(c_PrinterError);
		}
	return "";
	}

//---------------------------------------------------------------------------

private void showPrintError (int p_PrinterStatus, Exception p_Exception, String[] p_Filler)
	{
	String l_PrinterStatus;
	
	if (p_Exception == null)
		{
		l_PrinterStatus = this.getMeaningOf (p_PrinterStatus);
		
		ErrorDialog.showErrorDialog	(MainFrame.getInstance(), 
									 Translatrix.getTranslationString(c_PrintErrorTitle), 
									 Translatrix.getTranslationString(c_PrintErrorMessage,p_Filler),
									 l_PrinterStatus);
		}
	else	
		{
		ErrorDialog.showErrorDialog	(MainFrame.getInstance(), 
									 Translatrix.getTranslationString(c_PrintErrorTitle), 
									 Translatrix.getTranslationString(c_PrintErrorMessage,p_Filler),
									 p_Exception);
		
		}
	}
						

//---------------------------------------------------------------------------

//***************************************************************************
//* Swing Worker Thread                                                     *
//***************************************************************************
//---------------------------------------------------------------------------

Object BatchPrinter ()
	{
	this.batchPrintInvoices();
	return "";
	}

//---------------------------------------------------------------------------

private synchronized void batchPrintInvoices ()
	{
//	InvoiceInterface		l_InvoiceInterface;
	
	Iterator <InvoiceStub>  l_StubIterator;
	InvoiceStub				l_Stub;
	Invoice			    	l_Invoice;
	InvoicePrinter			l_Printer;
	int						l_InvoiceCount = 0;
	int						l_BatchCount   = 0;
	int						l_PrintFrom	   = 0;
	int						l_PrintTo	   = 0;
	
	boolean					l_Done		   = false;
	boolean					l_WasPrinted   = false;
	
	int						l_Action;
	String []				l_Filler;
	
	HashMap<String, Object> l_Parameter;
	
	if (m_InvoiceStubUtils == null) m_InvoiceStubUtils = new InvoiceStubUtils ();
	
	this.togglePrintButton(true);
	
	if ((Boolean)MainFrame.getInstance().userSettings.getValue(UserSettingsPlugin.PRINTER_BATCH_SPLIT_ENABLED))
		 m_BatchSize = (Integer) MainFrame.getInstance().userSettings.getValue(UserSettingsPlugin.PRINTER_BATCH_SIZE);
	else m_BatchSize = 0;	
	
	m_Paused	= false;
	m_Suspend   = false;
	
	//========================================================================
	//= Step 1. Get instance of InvoicePrinter object. In the case of batch
	//= print, we don't want neither a preview nor a print dialog. Setting
	//= InvoicePrinters' print immediately flag makes sure they won't show.
	//========================================================================
	
	l_Printer = new InvoicePrinter ();
	l_Printer.setPrintImmediately(true);
	l_Printer.setQueueSizeRetries (6);
	l_Printer.setQueueSizeDelay (10000);
	l_Printer.setJobAcceptanceRetries (10);
	l_Printer.setJobAcceptanceDelay (1000);
	
	//========================================================================
	//= Step 2. Check whether user specified to print all invoices (default)
	//= or whether he wishes to print a certain range of invoices.
	//========================================================================

	m_PrintInvoicesProgress.setMinimum(0);

	if (!m_PrintAll.isSelected())
		{
		// Minus 1 because Invoice positions on statement start with one
		// and internally we're using 0 based indexes.
		
		l_PrintFrom = this.getPrintFrom() - 1;
		l_PrintTo = this.getPrintTo() - 1;			
		
		m_PrintInvoicesProgress.setMaximum(l_PrintTo - l_PrintFrom + 1);
		}
	else
		{
		m_PrintInvoicesProgress.setMaximum(m_InvoiceStubs.size());
		}
	
	m_Timer = new Timer (c_TimerDelay,this);
	
	//========================================================================
	//= Step 3. Next step consists in iterating over all the invoices.
	//= Each invoice will be fed to the invoice workflow to make sure it
	//= may be printed. If so, the invoice will be in printed state.
	//= After the invoice is printed, it will be saved to keep track of its 
	//= state change.
	//========================================================================
	
	l_Parameter = PrintParameterFetcher.createDefaultPrintParameter();
	
	l_StubIterator = m_InvoiceStubs.iterator();
	while (l_StubIterator.hasNext() && !l_Done && !m_Aborted)
		{		
		//====================================================================
		//= Step 4. If user specified a print from boundary, then we have to
		//= fast forward iterator to specified position.
		//====================================================================
			
		if (l_PrintFrom > 0)
			{
			l_Stub = l_StubIterator.next();
			l_PrintFrom--;
			l_InvoiceCount++;
			continue;
			}
		
		//====================================================================
		//= Step 5. If user specified a print to boundary, then we have to
		//= make sure we stop at the desired location.
		//====================================================================

		if ((l_PrintTo > 0) && (l_InvoiceCount >= l_PrintTo)) l_Done = true;
		
		//====================================================================
		//= Step 6. If user specified in his settings that he wishes to split
		//= batch print jobs in smaller chuncks.
		//====================================================================

		if (m_Suspend || ((m_BatchSize > 0) && (l_BatchCount == m_BatchSize)))
			{
			l_Filler = new String [2];
			l_Filler [0] = new Integer(l_InvoiceCount).toString();
			l_Filler [1] = Integer.valueOf(m_InvoiceStubs.size()).toString();
			
			m_PrintInvoicesProgress.setString (Translatrix.getTranslationString(c_PrintProgressPaused,l_Filler));	
			this.suspendBatchPrinting();
			l_BatchCount = 0;
			m_Suspend = false;
			}			
						
		l_Stub = l_StubIterator.next();
		l_Invoice = m_InvoiceStubUtils.fetchInvoiceForStub (l_Stub,m_Dependencies);
		if (l_Invoice != null)
			{
			l_Invoice.monetize();
		
			m_Timer.restart();
			
			l_Action = InvoiceWorkflow.changeInvoiceState(l_Invoice, InvoiceWorkflow.c_PrintAction);
			if (	(l_Action == InvoiceWorkflow.c_DoPrint)
//				 || (l_Action == InvoiceWorkflow.c_DoPrintCopy))
				 || (l_Action == InvoiceWorkflow.c_DoPrintOriginal))
				{
				m_Logger.log(Level.INFO, "Printing Invoice for " + l_Stub.getPatientName() + " " + l_Stub.getPatientFirstName());
				
				m_PrintInvoicesProgress.setString(l_Invoice.formatInvoiceNumber(Invoice.c_LongFormat, true));
				m_PrintInvoicesProgress.setValue(l_InvoiceCount);
			
				try	{
					l_Parameter.put(PrintParameter.PRINT_PARAMETER_PATIENT,	l_Invoice.getPatient());
					l_Parameter.put(PrintParameter.PRINT_PARAMETER_PHYSICIAN,l_Invoice.getPhysician());
					l_Parameter.put(PrintParameter.PRINT_PARAMETER_INVOICE,	l_Invoice);
					
					l_WasPrinted = false;
					l_WasPrinted = l_Printer.printInvoice(l_Invoice, false, false, l_Parameter);
					if (l_WasPrinted)
						{
//						if (!l_Invoice.alreadyPrinted()) l_Invoice.setPrintDate(new Date());
						l_Invoice = m_InvoiceStubUtils.saveInvoice (l_Invoice);
						}
					else
						{
						l_Filler = new String [3];
						l_Filler[0] = m_Statement.formatStatementNumber();
						l_Filler[1] = l_Invoice.formatInvoiceNumber(Invoice.c_LongFormat, true);
						l_Filler[2] = Integer.valueOf (l_InvoiceCount).toString();
						
						this.showPrintError (l_Printer.getStatus(), l_Printer.getException(), l_Filler);
						m_Aborted = true;
						}						
					}	
				catch (Exception p_Exception)
					{
					l_Filler = new String [3];
					l_Filler[0] = m_Statement.formatStatementNumber();
					l_Filler[1] = l_Invoice.formatInvoiceNumber(Invoice.c_LongFormat, true);
					l_Filler[2] = Integer.valueOf(l_InvoiceCount).toString();

					this.showPrintError (l_Printer.getStatus(), p_Exception, l_Filler);
					m_Logger.log(Level.ERROR, "Failed to save Invoice state\n" + p_Exception.getLocalizedMessage());
					m_Aborted = true;
					}
				}
			}
		l_InvoiceCount++;
		l_BatchCount++;
		}
	
	this.togglePrintButton(false);
	l_Printer.close();
	
	m_PrintInvoicesProgress.setValue(0);
	if (m_Aborted)
		{
		m_PrintInvoicesProgress.setString (Translatrix.getTranslationString(c_PrintProgressAbort));	
		}
	else
		{
		m_PrintInvoicesProgress.setString (Translatrix.getTranslationString(c_PrintProgressDone));
		this.setVisible (false);
		}
	}

//---------------------------------------------------------------------------

private void startBatchPrinting ()
	{
	m_BatchPrinter = new SwingWorker() 
	{
	public Object construct() 
		{
		return BatchPrinter ();
		}
	public void start ()
		{
		m_Aborted = false;
		super.start();
		}
	public void finished ()
		{
		m_InvoicesWerePrinted = true;
		}
	public void interrupt ()
		{
		m_Aborted = true;
		super.interrupt();
		}
	};

	m_BatchPrinter.start ();  	
	}

//---------------------------------------------------------------------------

private void abortBatchPrinting ()
	{
	if (m_BatchPrinter != null) m_BatchPrinter.interrupt();
	}

//---------------------------------------------------------------------------

private synchronized void suspendBatchPrinting ()
	{
	m_Paused = true;
	this.toggleResumeButton(true);
	while (m_Paused) 
		try {wait(500);} 
		catch (Exception p_Exception) 
			{
			m_Logger.log(Level.ERROR, "Exception while suspended!", p_Exception);
			m_Paused = false;
			};
			
	this.toggleResumeButton(false);
	}

//---------------------------------------------------------------------------

private void resumeBatchPrinting ()
	{
	m_Paused = false;
	}

//---------------------------------------------------------------------------
//***************************************************************************
//* Class Body		                                                        *
//***************************************************************************
//---------------------------------------------------------------------------

public void setStatement (Statement p_Statement) {
	String	l_PeriodBegin;
	String	l_PeriodEnd;
	
	m_Statement 	 = p_Statement;
	
	m_ListWasPrinted 	  = false;
	m_InvoicesWerePrinted = false;
	
	if (p_Statement == null) return;
		
	m_StatementReference.setText (m_Statement.formatStatementNumber());
	
	l_PeriodBegin = Translatrix.getTranslationString(c_StatementFromLabel) + " " +
			   		m_DateFormat.format(m_Statement.getStartDate());
	l_PeriodEnd =   Translatrix.getTranslationString(c_StatementToLabel) + " " +
			   		m_DateFormat.format(m_Statement.getEndDate());
	
	m_StatementFrom.setText(l_PeriodBegin);
	m_StatementTo.setText(l_PeriodEnd);
	
	m_StatementPayer.setText (m_Statement.formatThirdPartyPayers());
	
	if (m_Statement.getState().intValue() >= StatementWorkflow.c_PrintedState) {
		this.setEnabled (m_PrintInvoicesPanel, true);
		m_PrintAll.setSelected(true);
		m_PrintFrom.setEnabled(false);
		m_PrintTo.setEnabled(false);
	} else this.setEnabled (m_PrintInvoicesPanel, false);
}

//---------------------------------------------------------------------------

public void setInvoiceStubs (Collection <InvoiceStub> p_InvoiceStubs)
	{
	Iterator <InvoiceStub>	l_StubIterator;
	InvoiceStub				l_Stub;
	String					l_NumberOfInvoices;
//	Long					l_TotalAmount = 0L;
	BigDecimal				l_TotalAmount;
	
	m_InvoiceStubs = p_InvoiceStubs;
	l_TotalAmount  = new BigDecimal (0);
	
	if (p_InvoiceStubs == null) return;
	
	l_NumberOfInvoices = "";
	
	l_StubIterator = p_InvoiceStubs.iterator();
	while (l_StubIterator.hasNext())
		{
		l_Stub = l_StubIterator.next();
		l_TotalAmount = InvoiceStub.addToTotal(l_TotalAmount,l_Stub.getAmount());
		}
	
	l_NumberOfInvoices += m_InvoiceStubs.size();
	
	m_StatementInvoices.setText (l_NumberOfInvoices);
	m_StatementAmount.setText(m_CurrencyFormat.format(InvoiceStub.getTotal(l_TotalAmount)));
	
	if (m_InvoiceStubs.size() > 0) {
		m_PrintFrom.setModel(new SpinnerNumberModel (1,1,m_InvoiceStubs.size(),1));
		m_PrintTo.setModel(new SpinnerNumberModel (m_InvoiceStubs.size(),1,m_InvoiceStubs.size(),1));
	} else {
		m_PrintFrom.setModel(new SpinnerNumberModel (1,1,1,1));
		m_PrintTo.setModel(new SpinnerNumberModel (1,1,1,1));
	}
	}

//---------------------------------------------------------------------------

public boolean invoiceListWasPrinted ()
	{
	return m_ListWasPrinted;
	}

//---------------------------------------------------------------------------

public boolean invoicesWerePrinted ()
	{
	return m_InvoicesWerePrinted;
	}

//---------------------------------------------------------------------------

public void relocalize() 
	{
    Locale  l_Locale;
  	
    l_Locale = Translatrix.getLocale ();
    	  	
    if (l_Locale != null)
         m_DateFormat = new SimpleDateFormat ("d MMMM yyyy",l_Locale);
    else m_DateFormat = new SimpleDateFormat ("d MMMM yyyy");   
 
    m_CurrencyFormat = new CurrencyFormat (Currency.getInstance ("EUR"));

	if (m_StatementBorder != null) 
		m_StatementBorder.setTitle(Translatrix.getTranslationString(c_StatementBorder));
	
	if (m_PrintListBorder != null) 
		m_PrintListBorder.setTitle(Translatrix.getTranslationString(c_PrintListBorder));
	
	if (m_PrintInvoicesBorder != null) 
		m_PrintInvoicesBorder.setTitle(Translatrix.getTranslationString(c_PrintInvoicesBorder));
	
	if (m_StatementReferenceLabel != null) 
		m_StatementReferenceLabel.setText(Translatrix.getTranslationString(c_StatementReferenceLabel));
	
	if (m_StatementPeriodLabel != null) 
		m_StatementPeriodLabel.setText(Translatrix.getTranslationString(c_StatementPeriodLabel));
	
	if (m_StatementPayerLabel != null) 
		m_StatementPayerLabel.setText(Translatrix.getTranslationString(c_StatementPayerLabel));
	
	if (m_StatementInvoicesLabel != null) 
		m_StatementInvoicesLabel.setText(Translatrix.getTranslationString(c_StatementInvoicesLabel));
	
	if (m_StatementAmountLabel != null) 
		m_StatementAmountLabel.setText(Translatrix.getTranslationString(c_StatementAmountLabel));
	
	if (m_PrintAll != null) 
		m_PrintAll.setText(Translatrix.getTranslationString(c_PrintAllLabel));

	if (m_PrintFromLabel != null) 
		m_PrintFromLabel.setText(Translatrix.getTranslationString(c_PrintFromLabel));
	
	if (m_PrintToLabel != null) 
		m_PrintToLabel.setText(Translatrix.getTranslationString(c_PrintToLabel));

	if (m_CancelButton != null) 
		m_CancelButton.setText(Translatrix.getTranslationString("core.cancel"));
	}

//---------------------------------------------------------------------------

public void actionPerformed(ActionEvent p_Event) 
	{
	if (p_Event.getSource().equals (m_PrintListButton))
		{
		this.printInvoiceList();
		this.setVisible (false);
		}
	else if (p_Event.getSource().equals(m_PrintInvoicesButton))
		{		
		if (m_PrintInvoicesButton.getText().equals(Translatrix.getTranslationString(c_PrintInvoicesButton)))
			{
			this.startBatchPrinting();
			}
		else if (m_PrintInvoicesButton.getText().equals(Translatrix.getTranslationString(c_PrintSuspendButton)))
			{
			m_Suspend = true;
			}
		else if (m_PrintInvoicesButton.getText().equals(Translatrix.getTranslationString(c_PrintResumeButton)))
			{
			this.resumeBatchPrinting();
			}	
		}
	else if (p_Event.getSource().equals (m_CancelButton))
		{
		this.abortBatchPrinting();
		this.setVisible (false);
		}
	else if (p_Event.getSource().equals (m_Timer))
		{
		m_PrintInvoicesProgress.setString (Translatrix.getTranslationString(c_WaitingForPrinter));	
		}
	}

//---------------------------------------------------------------------------

public void itemStateChanged (ItemEvent p_Event) 
	{
	boolean l_EnableIt;
	
	l_EnableIt = (p_Event.getStateChange() == ItemEvent.DESELECTED)?true:false; 

	if (	(m_PrintFromLabel == null) || (m_PrintToLabel == null)
		 || (m_PrintFrom	  == null) || (m_PrintTo      == null)) return;
	
	if (p_Event.getSource().equals (m_PrintAll))
		{
		m_PrintFromLabel.setEnabled(l_EnableIt);
		m_PrintFrom.setEnabled(l_EnableIt);
		m_PrintToLabel.setEnabled(l_EnableIt);
		m_PrintTo.setEnabled(l_EnableIt);
		
		if (l_EnableIt)
			{
			if ((m_InvoiceStubs != null) && (m_InvoiceStubs.size() > 0))
				{
				m_PrintFrom.setValue(Integer.valueOf(1));
				m_PrintTo.setValue((Integer)m_InvoiceStubs.size());
				}
			}
		}		
	}

//---------------------------------------------------------------------------

public void stateChanged (ChangeEvent p_Event) 
	{
	if (	(p_Event.getSource().equals (m_PrintFrom))
		 || (p_Event.getSource().equals (m_PrintTo)))
		{
		m_PrintAll.setSelected(false);
		}	
	}

//---------------------------------------------------------------------------	
//***************************************************************************
//* End of Class				                            				*
//***************************************************************************
//---------------------------------------------------------------------------	
}

