/*******************************************************************************
 * 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.Color;
import java.awt.Cursor;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Currency;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Locale;

import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;

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.beans.InvoiceBean;
import lu.tudor.santec.gecamed.billing.ejb.session.beans.StatementInvoiceStubBean;
import lu.tudor.santec.gecamed.billing.ejb.session.interfaces.InvoiceInterface;
import lu.tudor.santec.gecamed.billing.ejb.session.interfaces.StatementInvoiceStubInterface;
import lu.tudor.santec.gecamed.billing.gui.BillingModule;
import lu.tudor.santec.gecamed.billing.gui.event.invoice.InvoiceChangeEvent;
import lu.tudor.santec.gecamed.billing.gui.event.invoice.InvoiceEventDispatcher;
import lu.tudor.santec.gecamed.billing.gui.event.invoice.InvoiceEventSource;
import lu.tudor.santec.gecamed.billing.gui.event.invoice.InvoiceListener;
import lu.tudor.santec.gecamed.billing.gui.event.usermode.UserModeListener;
import lu.tudor.santec.gecamed.billing.gui.invoice.InvoiceListModel;
import lu.tudor.santec.gecamed.billing.gui.invoice.stub.GenericInvoiceStubListBox;
import lu.tudor.santec.gecamed.billing.gui.invoice.stub.InvoiceStubComparator;
import lu.tudor.santec.gecamed.billing.gui.invoice.stub.InvoiceStubUtils;
import lu.tudor.santec.gecamed.billing.gui.search.InvoiceStubSearchTableHeader;
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.GECAMedLog;
import lu.tudor.santec.gecamed.core.gui.MainFrame;
import lu.tudor.santec.gecamed.core.gui.utils.GECAMedGuiUtils;
import lu.tudor.santec.gecamed.core.gui.utils.GradientFactory;
import lu.tudor.santec.gecamed.core.gui.utils.SwingWorker;
import lu.tudor.santec.gecamed.core.gui.widgets.progress.ProgressDialog;
import lu.tudor.santec.gecamed.core.gui.widgets.searchtableheader.SearchTableHeader;
import lu.tudor.santec.gecamed.core.utils.ManagerFactory;
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                                                        *
//***************************************************************************

public class StatementInvoicesPanel extends JPanel implements InvoiceEventSource,
															  ListSelectionListener,
															  TableModelListener,
															  PropertyChangeListener,
															  KeyListener,
															  UserModeListener,
															  Relocalizable
{
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	private JLabel							m_Title;
	private JPanel							m_TitlePanel;
	
	private GenericInvoiceStubListBox		m_InvoiceStubListBox;
	private InvoiceStubListModel			m_InvoiceStubs;
	private InvoiceStubRenderer				m_InvoiceRenderer;	
	private SettlementEditorField			m_SettlementEditor;
	
	private InvoiceStubSearchTableHeader	m_SearchHeader;
	private PatientNameFilter				m_PatientNameFilter;
	
	private DateFormat						m_DateFormat;
	private CurrencyFormat					m_CurrencyFormat;
	
	private ImageIcon						m_OpenState;
	private ImageIcon						m_ClosedState;
	private ImageIcon						m_PrintedState;
	private ImageIcon						m_PaidState;
	private ImageIcon						m_CanceledState;
	
	private Statement						m_Statement;

	private InvoiceStubUtils				m_InvoiceStubUtils;
	
	private int								m_BulkOperation;
	private SwingWorker  					m_BulkWorker;
	private ProgressDialog					m_ProgressDialog;
	
	private Long							m_InvoiceCount;
	
	private boolean							m_Busy;
	private boolean							m_Aborted;
	
//	private Collection <InvoiceListener> 	m_InvoiceListeners;
	private InvoiceEventDispatcher			m_InvoiceListeners;

private MouseEvent event;

private JPopupMenu popup;

	
	private static final Collection <Integer> m_Dependencies = new ArrayList <Integer> ();
    
	static 	{
    		m_Dependencies.add(InvoiceInterface.c_PatientDependency);
    		m_Dependencies.add(InvoiceInterface.c_CloserDependency);
    		m_Dependencies.add(InvoiceInterface.c_ModifierDependency);
	    	}

	private static Logger m_Logger = Logger.getLogger ("gecamed.billing.gui.statement.StatementInvoicesPanel");

//***************************************************************************
//* Class Constants                                                         *
//***************************************************************************
	
	public static final int	c_FetchInvoices	= 1;
	public static final int	c_CloseInvoices	= 2;
	public static final int	c_OpenInvoices	= 3;
	
	public static final String c_InvoiceSettled		= "StatementInvoicesPanel.InvoiceSettled";
	public static final String c_InvoiceRevoked		= "StatementInvoicesPanel.InvoiceRevoked";
		
	private final static int	c_InvoiceChunkSize = 10;
	
	private final static int	c_InterruptWaitTime	= 500;
	private final static int	c_MaxWaitCycles		= 10;
	
	private final static String c_Columns= 	"1dlu,fill:pref:grow,1dlu";
	
	private final static String c_Rows=    	"1dlu,fill:max(20dlu;pref)," +
											"1dlu,fill:pref:grow,1dlu";

//***************************************************************************
//* Constructor(s)                                                          *
//***************************************************************************
//---------------------------------------------------------------------------

public StatementInvoicesPanel ()
	{
	CellConstraints		l_Constraints;
	FormLayout			l_Layout;
	EmptyBorder			l_Padding;
	LineBorder			l_Frame;
	CompoundBorder		l_Border;
	MouseAdapter		l_MouseAdapter;
	
	l_Layout = new FormLayout(c_Columns, c_Rows);
	this.setLayout(l_Layout);
	this.setOpaque(false);
	
	l_Constraints  = new CellConstraints();

	l_Padding = new EmptyBorder (2,5,2,2);
	l_Frame   = new LineBorder (GECAMedColors.c_ModuleBorderColor);
	l_Border = new CompoundBorder (l_Frame,l_Padding);

	m_Title = new JLabel ();
	m_Title.setOpaque(true);
	m_Title.setBackground(BillingModule.m_ModulePlainModeColor);
	m_Title.setBorder(l_Border);
	m_Title.setText(Translatrix.getTranslationString("StatementInvoicesPanel.NewStatement"));
	
	m_TitlePanel = GradientFactory.makeGradient (m_Title, BillingModule.m_ModulePlainModeColor, true);
	
    m_OpenState     = BillingModule.getIconResource("stmnt_open_state.png");
    m_ClosedState   = BillingModule.getIconResource("stmnt_closed_state.png");
    m_PrintedState  = BillingModule.getIconResource("stmnt_printed_state.png");
    m_PaidState     = BillingModule.getIconResource("stmnt_paid_state.png");
    m_CanceledState = null;

 	l_MouseAdapter = new MouseAdapter()
		{
		public void mouseClicked(MouseEvent p_Event)
			{
			if (p_Event.getClickCount() == 2)
				{
				editSelectedInvoice ();
				}
			}
		
			public void mouseReleased(MouseEvent e)
			{
				if (e.isPopupTrigger())
				{
					showMenu(e);
				}
			}
			
			public void mousePressed(MouseEvent e)
			{
				if (e.isPopupTrigger())
				{
					showMenu(e);
				}
			}
		
		};
	
	m_InvoiceStubs = new InvoiceStubListModel ();
	m_InvoiceStubs.addTableModelListener(this);
	m_InvoiceStubs.setInsurances(BillingModule.getInstance().getHealthInsurances());
	
	m_InvoiceRenderer = new InvoiceStubRenderer ();
	m_SettlementEditor = new SettlementEditorField ();
	
	m_InvoiceStubListBox = new GenericInvoiceStubListBox (m_InvoiceStubs);
	m_InvoiceStubListBox.setInvoiceStubRenderer(m_InvoiceRenderer);
	m_InvoiceStubListBox.setTableCellEditor (m_SettlementEditor,InvoiceStubListModel.c_SettlementColumn);
	
	m_InvoiceStubListBox.getViewport().setOpaque(false);
	m_InvoiceStubListBox.setBackground(GECAMedColors.c_ScrollPaneBackground);
	m_InvoiceStubListBox.addListSelectionListener(this);
	m_InvoiceStubListBox.addKeyListener(this);
	m_InvoiceStubListBox.addMouseListener(l_MouseAdapter);

    m_SearchHeader = new InvoiceStubSearchTableHeader (m_InvoiceStubListBox.getTable());
    m_SearchHeader.addPropertyChangeListener(this);
	
	m_SearchHeader.setEnabled (InvoiceStubListModel.c_PatientNameColumn, true);
	m_SearchHeader.setMutualExclusive (InvoiceStubListModel.c_PatientNameColumn, true);
	
	m_InvoiceStubListBox.getTableSorter().setTableHeader (m_InvoiceStubListBox.getTable().getTableHeader());
	m_InvoiceRenderer.setSearchHeader (m_SearchHeader);
	
	m_PatientNameFilter = new PatientNameFilter ();
	m_InvoiceStubs.installFilter(m_PatientNameFilter, InvoiceStubListModel.c_PatientNameColumn);
	
	this.relocalize();	
	
	this.add (m_TitlePanel, l_Constraints.xywh(2, 2, 1, 1));
	this.add (m_InvoiceStubListBox, l_Constraints.xywh(2, 4, 1, 1));
	
	m_InvoiceListeners = new InvoiceEventDispatcher ();
	
	m_Busy = false;
	m_ProgressDialog = new ProgressDialog ();
	
	BillingModule.getInstance().addUserModeListener(this);
	}

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

private StatementInvoiceStubInterface getInvoiceStubInterface ()
	{
	StatementInvoiceStubInterface l_InvoiceStubInterface = null;
	
	try {
		l_InvoiceStubInterface = (StatementInvoiceStubInterface) ManagerFactory.getStatefulRemote(StatementInvoiceStubBean.class);
		} 
	catch (Exception p_Exception) 
		{
		m_Logger.warn(p_Exception.getLocalizedMessage());
		}

	return l_InvoiceStubInterface;
	}

private InvoiceInterface getInvoiceInterface ()
{
	InvoiceInterface l_InvoiceInterface = null;

try {
	l_InvoiceInterface = (InvoiceInterface) ManagerFactory.getStatefulRemote(InvoiceBean.class);
	} 
catch (Exception p_Exception) 
	{
	m_Logger.warn(p_Exception.getLocalizedMessage());
	}

return l_InvoiceInterface;
}
	
//---------------------------------------------------------------------------

private void releaseInvoiceStubInterface (StatementInvoiceStubInterface p_Interface)
	{
	if (p_Interface == null) return;
	
	try {
		p_Interface.remove();
		}
	catch (Exception p_Exception)	
		{
		m_Logger.log(Level.INFO,"StatementInvoiceStubBean Removed!",p_Exception);
		}
	}

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

private Invoice fetchInvoiceForStub (InvoiceStub p_InvoiceStub)
	{
	if (m_InvoiceStubUtils == null) m_InvoiceStubUtils = new InvoiceStubUtils ();
	
	return m_InvoiceStubUtils.fetchInvoiceForStub(p_InvoiceStub, m_Dependencies);
	}

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

private InvoiceStub fetchStubForInvoice (Invoice p_Invoice)
	{
	StatementInvoiceStubInterface l_InvoiceStubInterface;	
	InvoiceStub					  l_Stub = null;
	
	l_InvoiceStubInterface = this.getInvoiceStubInterface();
	if (l_InvoiceStubInterface == null) return l_Stub;
	
	try	{
		l_Stub = l_InvoiceStubInterface.getInvoiceStubById(p_Invoice.getId());
		}
	catch (Exception p_Exception)
		{
		m_Logger.warn(p_Exception.getLocalizedMessage());
		}
	
	this.releaseInvoiceStubInterface(l_InvoiceStubInterface);

	return l_Stub;
	}

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

//private Long getSettledInvoiceCountForStatement (Statement p_Statement)
//	{
//	Long	l_Count = Long.valueOf(0);
//	
//	StatementInvoiceStubInterface	l_InvoiceStubInterface = null;
//	
//	l_InvoiceStubInterface = this.getInvoiceStubInterface();
//	
//	if (l_InvoiceStubInterface == null) return l_Count;
//	
//	try {
//		l_Count = l_InvoiceStubInterface.settledInvoiceCountForStatement(p_Statement);
//		}
//	catch (Exception p_Exception) 
//		{
//		m_Logger.warn(p_Exception.getLocalizedMessage());
//		}
//	
//	return l_Count;
//	}

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

private Collection <InvoiceStub> sortInvoiceStubs (Collection <InvoiceStub> p_InvoiceStubs)
	{
	InvoiceStub [] 				l_StubArray;
	LinkedHashSet <InvoiceStub> l_SortedStubs;
	InvoiceStubComparator		l_Comparator;
	int							l_Index;
	
	if (p_InvoiceStubs == null) return null;
	
	l_StubArray = new InvoiceStub[p_InvoiceStubs.size()];
	l_StubArray = p_InvoiceStubs.toArray(l_StubArray);
	
	l_Comparator = new InvoiceStubComparator ();
	l_Comparator.addSortCriterion(InvoiceStubComparator.c_PatientFullName, InvoiceStubComparator.c_Ascending);
		
	Arrays.sort (l_StubArray,l_Comparator);

	l_SortedStubs = new LinkedHashSet <InvoiceStub> (l_StubArray.length);
	
	for (l_Index = 0; l_Index < l_StubArray.length; l_Index++)
		{
		l_SortedStubs.add(l_StubArray[l_Index]);
		}

	return l_SortedStubs;
	}

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

private void editSelectedInvoice ()
	{
	Invoice 	l_Invoice;
	
	l_Invoice = this.getSelectedInvoice();
	if (l_Invoice != null) BillingModule.getInstance().editInvoice(l_Invoice);		
	}

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

private void excludeInvoice (Invoice p_Invoice)
	{
	Statement			l_Statement;
	
	if (p_Invoice == null) return;
	
	if (m_InvoiceStubUtils == null) m_InvoiceStubUtils = new InvoiceStubUtils ();
	
	l_Statement = p_Invoice.getStatement();
	if (l_Statement == null) return;
	
	try {
		p_Invoice.setStatement(null);
		p_Invoice = m_InvoiceStubUtils.saveInvoice(p_Invoice);
		
		BillingModule.getInstance().fireInvoiceChange (InvoiceChangeEvent.c_InvoiceStatusChanged, p_Invoice);
		
		GECAMedLog.user ("Billing","EXCLUDE Invoice","Exclude Invoice with ID " + p_Invoice.getId() + " from statement " + l_Statement.formatStatementNumber());
		}
	catch (Exception p_Exception) 
		{
		m_Logger.warn(p_Exception.getLocalizedMessage());
		}
	}

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

private void reflectStatementState (Statement p_Statement)
	{
//	String	l_StatementStateName;
	
	if (p_Statement == null) return;
	
//	l_StatementStateName = StatementWorkflow.getStatementStateName(p_Statement);
	
	switch (p_Statement.getState())
	{
	case StatementWorkflow.c_NewState :
	case StatementWorkflow.c_OpenState :
	
		m_Title.setIcon(m_OpenState);
		//m_Invoices.setEditable(false);
		
		break;
	
	case StatementWorkflow.c_ClosedState :
		
		m_Title.setIcon(m_ClosedState);
		//m_Invoices.setEditable(false);
		break;
	
	case StatementWorkflow.c_PrintedState :					
		
		m_Title.setIcon(m_PrintedState);
		//m_Invoices.setEditable(true);
		break;
	
	case StatementWorkflow.c_SettledState:
		
		m_Title.setIcon(m_PaidState);
		//m_Invoices.setEditable(true);
		break;
		
	case StatementWorkflow.c_CanceledState:
		
		m_Title.setIcon(m_CanceledState);
		//m_Invoices.setEditable(false);
		break;				
		}
	}

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

private void updateTitle (Statement p_Statement, boolean p_WithTotal)
	{
	String							l_StartDate;
	String							l_EndDate;
	String							l_TotalAmount;
	String[]						l_Filler;
	
	if (p_Statement == null) return;
	
	l_StartDate   = m_DateFormat.format(p_Statement.getStartDate());
	l_EndDate     = m_DateFormat.format(p_Statement.getEndDate());
	
	if (p_WithTotal) 
		 l_Filler = new String [6];
	else l_Filler = new String [4];
	
	l_Filler[0] = p_Statement.formatStatementNumber();
	l_Filler[1] = l_StartDate;
	l_Filler[2] = l_EndDate;
	l_Filler[3] = p_Statement.formatThirdPartyPayers();
	
	if (p_WithTotal) 
		{
		l_TotalAmount = m_CurrencyFormat.format (this.getTotalAmount());
		l_Filler[4] = Integer.valueOf(m_InvoiceStubs.getRowCount()).toString();
		l_Filler[5] = l_TotalAmount;
		m_Title.setText(Translatrix.getTranslationString("StatementInvoicesPanel.TitleWithTotal",l_Filler));
		}
	else m_Title.setText(Translatrix.getTranslationString("StatementInvoicesPanel.Title",l_Filler));
	
	
	}

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

public void updateListingForStatement (Statement p_Statement)
	{
	StatementInvoiceStubInterface	l_InvoiceStubInterface = null;
	
	l_InvoiceStubInterface = getInvoiceStubInterface ();
	
	if (l_InvoiceStubInterface == null) return;
	
	if (m_Busy) 
		{
		this.abortBulkOperation();
		this.waitUntilNoLongerBusy();
		}
		
	try {
		Long l_Count = l_InvoiceStubInterface.getInvoiceStubCountForStatement(p_Statement);
		m_InvoiceCount = l_Count;

		m_InvoiceStubListBox.removeAllInvoiceStubs();
		if (l_Count > 0) this.startBulkOperation(c_FetchInvoices);
		}
	catch (Exception p_Exception) 
		{
		m_Logger.warn(p_Exception.getLocalizedMessage());
		}
	
	this.releaseInvoiceStubInterface(l_InvoiceStubInterface);
	}

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

private double getTotalAmount ()
	{
	Iterator <InvoiceStub>	l_StubIterator;
	InvoiceStub				l_Stub;
//	Long					l_TotalAmount = 0L;
	BigDecimal				l_TotalAmount;
	
	l_TotalAmount = new BigDecimal (0);
	
	if (m_InvoiceStubs.getInvoiceStubs() != null)
		{
		l_StubIterator = m_InvoiceStubs.getInvoiceStubs().iterator();
		while (l_StubIterator.hasNext())
			{
			l_Stub = l_StubIterator.next();
			l_TotalAmount = InvoiceStub.addToTotal(l_TotalAmount, l_Stub.getAmount());	
			}
		}
	
	return InvoiceStub.getTotal(l_TotalAmount);
	}

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

Object InvoiceBulkOperation ()
	{
	switch (m_BulkOperation)
		{
		case c_FetchInvoices: this.fetchInvoicesForStatement();
		  					  break;
		case c_CloseInvoices: this.closeInvoicesForStatement();
		  					  break;
		case c_OpenInvoices:  this.openInvoicesForStatement();
		  					  break;
		}
	
	return "";
	}

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

private synchronized void fetchInvoicesForStatement ()
	{
	StatementInvoiceStubInterface	l_InvoiceStubInterface	= null;
	Collection <InvoiceStub>		l_BulkOfStubs			= null;
	Collection <InvoiceStub>		l_Stubs		 			= null;
	String[]						l_Filler;
	
	l_InvoiceStubInterface = getInvoiceStubInterface ();

	if (l_InvoiceStubInterface == null) return;

	l_BulkOfStubs = new LinkedHashSet <InvoiceStub> ();

	try {	
		l_InvoiceStubInterface.buildInvoiceStubQueryForStatement(m_Statement);
		l_InvoiceStubInterface.setFirstInvoiceStub(0);
		l_InvoiceStubInterface.setNumberOfInvoiceStubs (c_InvoiceChunkSize);
		
		if (m_InvoiceCount >= c_InvoiceChunkSize)
			{	
			l_Filler = new String[1];
			l_Filler [0] = m_InvoiceCount.toString();
			
			m_ProgressDialog.setCancelable(true);
			m_ProgressDialog.setProgressBarSpan(0,m_InvoiceCount.intValue());
		
			m_ProgressDialog.setTitle("StatementInvoicesPanel.FetchingInvoicesTitle");
			m_ProgressDialog.setMessage("StatementInvoicesPanel.FetchingInvoicesMessage",null);
			m_ProgressDialog.setTask("StatementInvoicesPanel.FetchingInvoicesTask", l_Filler);
			m_ProgressDialog.pack();
			
			MainFrame.showDialogCentered (m_ProgressDialog);
			}
		
		l_Filler = new String [2];
		
		do	{
			l_Stubs = l_InvoiceStubInterface.getNextInvoiceStubs();
			if (l_Stubs != null) 
				{
				l_BulkOfStubs.addAll(l_Stubs);
				
				if (m_InvoiceCount.intValue() > c_InvoiceChunkSize)
					{					
					l_Filler [0] = Integer.valueOf(l_BulkOfStubs.size()).toString();
					l_Filler [1] = m_InvoiceCount.toString();
					
					m_ProgressDialog.setProgress(l_BulkOfStubs.size(), 
												 "StatementInvoicesPanel.FetchingInvoicesProgress",
												 l_Filler);
					}
				
				m_Aborted = m_ProgressDialog.wasCanceled();
				}
			} 
		while (	   (m_Aborted == false)
				&& (l_Stubs != null) 
				&& (l_Stubs.size() == c_InvoiceChunkSize) );		
		
		if (m_Aborted) 
			{
			l_InvoiceStubInterface.close();
			}
		m_Busy = false;
		}
	catch (Exception p_Exception) 
		{
		l_InvoiceStubInterface.close();
		m_Logger.warn(p_Exception.getLocalizedMessage());
		}
	
	this.releaseInvoiceStubInterface(l_InvoiceStubInterface);

	m_ProgressDialog.setVisible(false);
	m_ProgressDialog.reset();
	
	if ((l_BulkOfStubs != null) && (m_Aborted == false)) 
		{
		l_Filler = new String[1];
		l_Filler [0] = m_InvoiceCount.toString();
				
		MainFrame.getInstance().showMessage(Translatrix.getTranslationString("StatementInvoicesPanel.InvoicesFetched",l_Filler));		
		
		l_BulkOfStubs = this.sortInvoiceStubs(l_BulkOfStubs);
		
		m_InvoiceStubs.setInvoiceStubs(l_BulkOfStubs);
		
		this.updateTitle(m_Statement, true);
		}
	
	MainFrame.getInstance().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
	}

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

private synchronized void closeInvoicesForStatement ()
	{
	StatementInvoiceStubInterface	l_InvoiceStubInterface	= null;
	Collection <Integer>			l_InvoiceIDs;
	String[]						l_Filler;
	
	l_InvoiceStubInterface = getInvoiceStubInterface ();
	
	if (l_InvoiceStubInterface == null) return;
	
	l_InvoiceIDs = InvoiceStub.getInvoiceIDs (m_InvoiceStubs.getInvoiceStubs());

	if (l_InvoiceIDs.size() >= c_InvoiceChunkSize)
		{	
		l_Filler = new String[1];
		l_Filler [0] = Integer.valueOf(l_InvoiceIDs.size()).toString();

		m_ProgressDialog.setCancelable(false);
		m_ProgressDialog.setProgressBarMode(true);
		m_ProgressDialog.setTitle("StatementInvoicesPanel.ClosingInvoicesTitle");
		m_ProgressDialog.setMessage("StatementInvoicesPanel.ClosingInvoicesMessage",null);
		m_ProgressDialog.setTask("StatementInvoicesPanel.ClosingInvoicesTask", l_Filler);
		m_ProgressDialog.pack();
		
		MainFrame.showDialogCentered (m_ProgressDialog);
		}
		
	try	{
		l_InvoiceStubInterface.closeInvoicesForStatement (m_Statement, l_InvoiceIDs);
		BillingModule.getInstance().fireMultipleInvoiceChange(InvoiceChangeEvent.c_InvoiceStatusChanged, l_InvoiceIDs);
		updateInvoiceListing ();
		}
	catch (Exception p_Exception) 
		{
		m_Logger.warn(p_Exception.getLocalizedMessage());
		}	
	
	this.releaseInvoiceStubInterface(l_InvoiceStubInterface);
	
	if (l_InvoiceIDs.size() >= c_InvoiceChunkSize)
		{	
		m_ProgressDialog.setVisible(false);
		m_ProgressDialog.reset();
		}
	
	MainFrame.getInstance().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
	}

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

private synchronized void openInvoicesForStatement ()
	{
	StatementInvoiceStubInterface	l_InvoiceStubInterface = null;	
	Collection <Integer>			l_InvoiceIDs;
	String[]						l_Filler;
	
	l_InvoiceStubInterface = getInvoiceStubInterface ();
	
	if (l_InvoiceStubInterface == null) return;
	
	l_InvoiceIDs = InvoiceStub.getInvoiceIDs (m_InvoiceStubs.getInvoiceStubs());

	if (l_InvoiceIDs.size() >= c_InvoiceChunkSize)
		{	
		l_Filler = new String[1];
		l_Filler [0] = Integer.valueOf(l_InvoiceIDs.size()).toString();

		m_ProgressDialog.setCancelable(false);
		m_ProgressDialog.setProgressBarMode(true);
		m_ProgressDialog.setTitle("StatementInvoicesPanel.OpeningInvoicesTitle");
		m_ProgressDialog.setMessage("StatementInvoicesPanel.OpeningInvoicesMessage",null);
		m_ProgressDialog.setTask("StatementInvoicesPanel.OpeningInvoicesTask", l_Filler);
		m_ProgressDialog.pack();
		
		MainFrame.showDialogCentered (m_ProgressDialog);
		}
		
	try	{
		l_InvoiceStubInterface.openInvoicesForStatement(m_Statement, l_InvoiceIDs);
		BillingModule.getInstance().fireMultipleInvoiceChange(InvoiceChangeEvent.c_InvoiceStatusChanged, l_InvoiceIDs);
		updateInvoiceListing ();
		}
	catch (Exception p_Exception) 
		{
		m_Logger.warn(p_Exception.getLocalizedMessage());
		}	
	
	this.releaseInvoiceStubInterface(l_InvoiceStubInterface);

	if (l_InvoiceIDs.size() >= c_InvoiceChunkSize)
		{	
		m_ProgressDialog.setVisible(false);
		m_ProgressDialog.reset();
		}
	
	
	
	MainFrame.getInstance().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
	}

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

private void startBulkOperation (int p_BulkOperation)
	{		
	m_BulkOperation = p_BulkOperation;
	
	m_BulkWorker = new SwingWorker() 
		{
		public Object construct() 
			{
			return InvoiceBulkOperation ();
			}
		public void start ()
			{
			m_Busy 	  = true;
			MainFrame.getInstance().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
			m_Aborted = false;
			super.start();
			}
		public void finished ()
			{
			MainFrame.getInstance().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
			m_Aborted = false;
			m_Busy    = false;
			m_InvoiceStubListBox.packColumns();
			}
		public void interrupt ()
			{
			m_Aborted = true;
			MainFrame.getInstance().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
			super.interrupt();
			}
		};

		m_BulkWorker.start ();  	
	}

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

private void abortBulkOperation ()
	{
	if (m_BulkWorker != null) m_BulkWorker.interrupt();
	}


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

private synchronized boolean waitUntilNoLongerBusy ()
	{
	int	l_WaitCycles = 0;
	
	while ((m_Busy) && (l_WaitCycles < c_MaxWaitCycles))
		{
		try	{
			this.wait (c_InterruptWaitTime);
			}
		catch (Exception p_Exception)
			{
			m_Logger.warn(p_Exception.getLocalizedMessage());
			}
		l_WaitCycles ++;		
		}
	
	if (l_WaitCycles < c_MaxWaitCycles) 
		 return true;
	else return false;
	}

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

public void addInvoiceListener (InvoiceListener p_Listener)
	{
	if ((m_InvoiceListeners == null) || (p_Listener == null)) return;
	
	m_InvoiceListeners.addInvoiceListener(p_Listener);
	}

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

public void removeInvoiceListener (InvoiceListener p_Listener)
	{
	if ((m_InvoiceListeners == null) || (p_Listener == null)) return;

	m_InvoiceListeners.removeInvoiceListener(p_Listener);
	}

//---------------------------------------------------------------------------
	
public void setStatement (Statement p_Statement, boolean forceReload)
	{
	
	if (p_Statement == null) return;
	
	this.updateTitle(p_Statement, false);
	this.reflectStatementState(p_Statement);
	
	if (forceReload || p_Statement.equals(m_Statement) == false)
		{
		m_Statement = p_Statement;
		this.updateListingForStatement (m_Statement);
		}
	else m_Statement = p_Statement;
	}

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

//public boolean allInvoicesSettled ()
//	{
//	Long	l_SettledInvoiceCount;
//	Long	l_TotalInvoiceCount;
//	boolean	l_FullySettled = false;
//	
//	if (m_Statement != null)
//		{
//		if (m_Busy) while(this.waitUntilNoLongerBusy() == false);
//		
//		l_SettledInvoiceCount = this.getSettledInvoiceCountForStatement(m_Statement);
//		l_TotalInvoiceCount = Integer.valueOf(m_InvoiceStubListBox.getNumberOfInvoiceStubs()).longValue();
//		l_FullySettled = l_SettledInvoiceCount.equals (l_TotalInvoiceCount);
//		}
//	
//	return l_FullySettled;
//	}

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

public void setEditable (boolean p_IsEditable)
	{
	m_InvoiceStubListBox.setEditable(p_IsEditable);
	m_InvoiceStubListBox.repaint();
	}

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

public void updateInvoiceListing ()
	{
	if (m_Statement != null)
		{
		this.updateListingForStatement (m_Statement);
		}
	}

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

public void removeSelectedInvoices ()
	{
	Invoice			l_Invoice;
	InvoiceStub []	l_SelectedStubs;
	String []		l_Filler;

	Integer			l_Length;
	int				l_UserChoice;
	int				l_InvoiceCount;
	
	l_UserChoice = JOptionPane.NO_OPTION;
	
	if (BillingModule.userHasPermission (BillingModule.c_BillingModule, BillingModule.c_editStatements))
		{
		l_SelectedStubs = m_InvoiceStubListBox.getSelectedInvoiceStubs();
		if (l_SelectedStubs == null) return;
		if (l_SelectedStubs.length == 1)
			{
			l_Filler = new String [2];
			l_Filler [0] = l_SelectedStubs[0].formatInvoiceNumber(false, true);
			l_Filler [1] = m_Statement.formatStatementNumber();
			
			l_UserChoice = BillingModule.getUserConfirmation("StatementInvoicesPanel.RemoveSingleInvoiceTitle",
					  							  			 "StatementInvoicesPanel.RemoveSingleInvoiceMessage",
					  							  			 l_Filler);
			}
		else
			{
			l_Filler = new String [2];
			
			l_Length = Integer.valueOf (l_SelectedStubs.length);
			l_Filler [0] = l_Length.toString();
			l_Filler [1] = m_Statement.formatStatementNumber();
			
			l_UserChoice = BillingModule.getUserConfirmation("StatementInvoicesPanel.RemoveMultipleInvoicesTitle",
					  							  			 "StatementInvoicesPanel.RemoveMultipleInvoicesMessage",
					  							  			 l_Filler);
			}
		
		if (l_UserChoice == JOptionPane.YES_OPTION) 
			{
			for (l_InvoiceCount=0; l_InvoiceCount < l_SelectedStubs.length; l_InvoiceCount++)
				{
				l_Invoice = this.fetchInvoiceForStub(l_SelectedStubs[l_InvoiceCount]);		
				if (l_Invoice != null) this.excludeInvoice (l_Invoice);
				}
			
			m_InvoiceStubListBox.removeSelectedInvoiceStubs();
			this.updateTitle(m_Statement, true);
			}
		}
	}

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

public void closeInvoices ()
	{
	if (m_Busy) 
		{
		this.abortBulkOperation();
		this.waitUntilNoLongerBusy();
		}

	this.startBulkOperation(c_CloseInvoices);
	}

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

public void openInvoices ()
	{
	if (m_Busy) 
		{
		this.abortBulkOperation();
		this.waitUntilNoLongerBusy();
		}

	this.startBulkOperation(c_OpenInvoices);
	}

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

public Invoice getSelectedInvoice ()
	{
	InvoiceStub	l_Stub;
	Invoice 	l_Invoice = null;
	
	l_Stub = m_InvoiceStubListBox.getSelectedInvoiceStub();
	if (l_Stub != null)
		{
		l_Invoice = this.fetchInvoiceForStub(l_Stub);
		if (l_Invoice != null) 
			{
			l_Invoice.setState (l_Stub.getState());
			l_Invoice.setOldState (l_Stub.getOldState());
			}
		}
	
	return l_Invoice;	
	}

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

public Invoice getInvoiceAt (int p_Row, boolean p_IsViewRow)
	{
	InvoiceStub	l_Stub;
	Invoice 	l_Invoice = null;
	
	l_Stub = m_InvoiceStubListBox.getInvoiceStubAt(p_Row,p_IsViewRow);
	if (l_Stub != null)
		{
		l_Invoice = this.fetchInvoiceForStub(l_Stub);
		if (l_Invoice != null) 
			{
			l_Invoice.setState (l_Stub.getState());
			l_Invoice.setOldState (l_Stub.getOldState());
			}
		}
	
	return l_Invoice;	
	}

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

public void setSelectedInvoice (Invoice p_Invoice)
	{
	InvoiceStub	l_Stub;
	
	l_Stub = this.fetchStubForInvoice(p_Invoice);
	if (l_Stub != null) m_InvoiceStubListBox.setSelectedInvoiceStub(l_Stub);
	}

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

public Collection <InvoiceStub> getInvoiceStubs ()
	{
	return m_InvoiceStubListBox.getInvoiceStubs();
	}

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

public void reset ()
	{
	m_Title.setText(Translatrix.getTranslationString("StatementInvoicesPanel.NewStatement"));
	m_InvoiceStubListBox.removeAllInvoiceStubs();
	m_Statement = null;
	}

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

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_DateFormat = GECAMedGuiUtils.getDateFormat(false);
	
    m_CurrencyFormat = new CurrencyFormat (Currency.getInstance ("EUR"));
	}

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

public void valueChanged(ListSelectionEvent p_SelectionEvent) 
	{
	Invoice				l_Invoice; 
	InvoiceChangeEvent	l_Event;
	
	
	if (!p_SelectionEvent.getValueIsAdjusting())
    	{    
		l_Invoice = this.getSelectedInvoice();
		
		l_Event = new InvoiceChangeEvent (this,InvoiceChangeEvent.c_InvoiceSelectionChanged,l_Invoice);
		m_InvoiceListeners.notifyInvoiceListeners (l_Event);
    	}
	}

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

public void keyPressed(KeyEvent p_Event) 
	{
	if(!p_Event.isConsumed())
		{
		switch (p_Event.getKeyChar())    
			{
			case KeyEvent.VK_BACK_SPACE :
			case KeyEvent.VK_DELETE     : this.removeSelectedInvoices ();
										  break;
			}
		}   
	}

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

public void keyReleased(KeyEvent p_Event) 
	{
	}

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

public void keyTyped(KeyEvent p_Event) 
	{
	}

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

public void tableChanged(TableModelEvent p_TableEvent) 
	{
	Invoice 	l_Invoice = null;
	int			l_Row;
	int			l_Column;
		
	if (p_TableEvent.getType() == TableModelEvent.UPDATE)
		{
		l_Row = p_TableEvent.getFirstRow();
		l_Column = p_TableEvent.getColumn();
		
		if (l_Column == InvoiceStubListModel.c_SettlementColumn)
			{		
			// Fireing TableModelEvents if table is sorted via TableSorter
			// causes selections being lost. This workaround makes sure
			// we have an actual selection.
			
			l_Row = m_InvoiceStubListBox.viewIndex(l_Row);
			m_InvoiceStubListBox.selectInvoiceStubAt(l_Row);			
			
			l_Invoice = this.getSelectedInvoice();
			if (l_Invoice != null)
				{				
				if (l_Invoice.getState().intValue() == InvoiceWorkflow.c_PaidState)
					{
					this.firePropertyChange (c_InvoiceSettled,null,l_Invoice);
					}
				else
					{
					this.firePropertyChange (c_InvoiceRevoked,null,l_Invoice);
					}
				}
			}
		}		
	}

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

public void userModeChanged(boolean p_SuperUserMode) 
	{
	Color				l_Background;
	CellConstraints		l_Constraints;
	
	l_Constraints = new CellConstraints ();
	
	l_Background = (p_SuperUserMode)?BillingModule.m_ModuleSuperModeColor
									:BillingModule.m_ModulePlainModeColor;

	m_Title.setBackground (l_Background);
	
	this.remove(m_TitlePanel);
	m_TitlePanel = GradientFactory.makeGradient (m_Title, l_Background, true);
	this.add (m_TitlePanel, l_Constraints.xywh(2, 2, 1, 1));
	}

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

public void propertyChange (PropertyChangeEvent p_Event) 
	{
	Integer l_Column;
	String	l_ColumnName;
	Object	l_Criterion;
	int		l_SortStatus;
	
	if (p_Event.getSource() == m_SearchHeader)
		{
		l_Column     = (Integer) p_Event.getOldValue();
		l_Criterion  = 			p_Event.getNewValue();
		l_ColumnName = InvoiceListModel.c_TableHeaders[l_Column];
		
		if (SearchTableHeader.c_ExclusiveCriterionChanged.equals (p_Event.getPropertyName()))
			{		
			if (l_Criterion == null)
				{
				m_PatientNameFilter.setFilter(null);
				m_InvoiceStubs.setFilteringEnabled(false, l_Column);
//				m_SearchHeader.resetTableHeaders();
				
				// The following is necessary to update the TableSorters' internal status
				// Not doing this will result in the TableSorter thinking there are less
				// Rows (due to previous filtering) then there are really.
		
				l_SortStatus = m_InvoiceStubListBox.getTableSorter().getSortingStatus(l_Column);
				m_InvoiceStubListBox.getTableSorter().setSortingStatus(l_Column, l_SortStatus);
		
				m_InvoiceStubListBox.revalidate();
				}
			else
				{
				if (l_Column.equals(InvoiceStubListModel.c_PatientNameColumn))
					{
					m_PatientNameFilter.setFilter(l_Criterion);
					m_InvoiceStubs.setFilteringEnabled(true, l_Column);
					m_InvoiceStubListBox.revalidate();
					}
				}
			}
		}
	}

/**
 * show a menu on right click
* @param c
 * @param x
 * @param y
 */
private void showMenu(MouseEvent e)
{
	if (! BillingModule.userHasPermission(BillingModule.c_BillingModule,BillingModule.c_editStatements))
		return;
	
	this.event = e;
	if (popup == null)
	{
		popup = new JPopupMenu();
		popup.add(new AbstractAction(Translatrix.getTranslationString("StatementInvoicesPanel.removeFromStatement"), BillingModule.getScaledIcon("remove_act.png", 16))
		{
			private static final long	serialVersionUID	= 1L;
			public void actionPerformed(ActionEvent ae)
			{
				Invoice inv = getSelectedInvoice();
				
				inv.setStatement(null);
				try {
					getInvoiceInterface().saveInvoice(inv);					
				} catch (Exception e2) {
					m_Logger.warn("Error saving Invoice");
					
				}
				updateListingForStatement (m_Statement);
			}
		});
		
	}
	
	int row = m_InvoiceStubListBox.getTable().rowAtPoint(event.getPoint());
	if (row < 0)
		return;
	
	int[] selectedRows = m_InvoiceStubListBox.getTable().getSelectedRows();
	if (selectedRows == null || selectedRows.length == 0)
	{
		m_InvoiceStubListBox.getTable().setRowSelectionInterval(row, row);
	}
	Invoice inv = getSelectedInvoice();
	if (inv == null || inv.getStatement() == null) {
		return;
	}
	
	popup.show(e.getComponent(), e.getX(), e.getY());
}

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