/*******************************************************************************
 * 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.trash;


import java.awt.Cursor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import javax.swing.AbstractButton;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;

import lu.tudor.santec.gecamed.billing.ejb.entity.beans.Invoice;
import lu.tudor.santec.gecamed.billing.ejb.entity.beans.TrashedInvoiceStub;
import lu.tudor.santec.gecamed.billing.ejb.session.beans.TrashedInvoiceStubBean;
import lu.tudor.santec.gecamed.billing.ejb.session.interfaces.TrashedInvoiceStubInterface;
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.invoice.stub.GenericInvoiceStubListBox;
import lu.tudor.santec.gecamed.core.gui.GECAMedColors;
import lu.tudor.santec.gecamed.core.gui.GECAMedLog;
import lu.tudor.santec.gecamed.core.gui.GECAMedTab;
import lu.tudor.santec.gecamed.core.gui.MainFrame;
import lu.tudor.santec.gecamed.core.gui.utils.SwingWorker;
import lu.tudor.santec.gecamed.core.gui.utils.WindowToolbox;
import lu.tudor.santec.gecamed.core.gui.widgets.CheckBoxEditorField;
import lu.tudor.santec.gecamed.core.gui.widgets.GECAMedBaseDialog;
import lu.tudor.santec.gecamed.core.gui.widgets.GenericListBox;
import lu.tudor.santec.gecamed.core.gui.widgets.searchtableheader.SearchResetAction;
import lu.tudor.santec.gecamed.core.gui.widgets.searchtableheader.SearchTableHeader;
import lu.tudor.santec.gecamed.core.gui.widgets.searchtableheader.SearchableTable;
import lu.tudor.santec.gecamed.core.utils.ManagerFactory;
import lu.tudor.santec.gecamed.core.utils.querybuilder.HibernateCondition;
import lu.tudor.santec.gecamed.core.utils.querybuilder.HibernateOperator;
import lu.tudor.santec.gecamed.core.utils.querybuilder.WhereClause;
import lu.tudor.santec.gecamed.usermanagement.ejb.entity.beans.GecamedUser;
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 TrashPanel extends GECAMedTab implements 	InvoiceEventSource,
														SearchableTable,
														ActionListener,
														TableModelListener,
														ListSelectionListener,
														PropertyChangeListener,
														Relocalizable
	{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	private GenericListBox					m_TrashedInvoiceListBox;
	private TrashedInvoiceStubListModel		m_TrashedInvoices;
	private TrashedInvoiceStubRenderer		m_TrashedInvoiceRenderer;	
	private TrashedInvoiceSearchTableHeader	m_SearchHeader;
	
	private JButton							m_UndeleteButton;
	
	private TrashedInvoiceStubInterface		m_TrashedInvoiceStubInterface;
	
	private SwingWorker						m_SearchWorker;
	private InvoiceEventDispatcher			m_InvoiceListeners;

	private JButton m_ClearButton;

	private TrashClearDialog trashClearDialog;
	
//***************************************************************************
//* Class Constants                                                         *
//***************************************************************************
	
	private final static String c_Columns= 	"3dlu,fill:pref:grow,3dlu,fill:max(50dlu;pref)";
	
	private final static String c_Rows=    	"3dlu,fill:max(10dlu;pref)," + 
											"3dlu,fill:max(10dlu;pref)," +
											"3dlu,fill:max(10dlu;pref),fill:pref:grow";

	private static final String c_UndeleteButton    	 = "TrashPanel.UndeleteButton";
	private static final String c_UndeleteTip    	 	 = "TrashPanel.UndeleteTip";
	private static final String c_ClearTrashButton    	 = "TrashPanel.ClearTrashButton";
	private static final String c_ClearTrashTip    	 	 = "TrashPanel.ClearTrashTip";
	

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

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

public TrashPanel (Collection <GecamedUser> p_Users)
	{
	CellConstraints		l_Constraints;
	FormLayout			l_Layout;
	TableColumn			l_Column;
	TableColumnModel	l_ColumnModel;
	ImageIcon			l_Icon;
	
	l_Constraints  	= new CellConstraints();
	l_Layout		= new FormLayout(c_Columns, c_Rows);

	this.setLayout(l_Layout);
	
	this.setOpaque(false);

	m_TrashedInvoices = new TrashedInvoiceStubListModel ();
	m_TrashedInvoices.addTableModelListener(this);
	
	m_TrashedInvoiceRenderer = new TrashedInvoiceStubRenderer ();
	m_TrashedInvoiceRenderer.setTrasher(p_Users);
	
	m_TrashedInvoiceListBox = new GenericListBox (m_TrashedInvoices);
	m_TrashedInvoiceListBox.setRenderer(m_TrashedInvoiceRenderer);
	
	m_SearchHeader = new TrashedInvoiceSearchTableHeader (m_TrashedInvoiceListBox.getTable());
	m_SearchHeader.addPropertyChangeListener(this);  
	
	m_TrashedInvoiceRenderer.setSearchHeader(m_SearchHeader);
	  	
	l_ColumnModel = m_TrashedInvoiceListBox.getTable().getColumnModel(); 
	
	l_Column = l_ColumnModel.getColumn(TrashedInvoiceStubListModel.c_TickedColumn);
	l_Column.setCellEditor(new CheckBoxEditorField());
	
	m_TrashedInvoiceListBox.getViewport().setOpaque(false);
	m_TrashedInvoiceListBox.setBackground(GECAMedColors.c_ScrollPaneBackground);
 
	m_TrashedInvoiceListBox.setSorting(TrashedInvoiceStubListModel.c_PatientNameColumn, GenericInvoiceStubListBox.c_Ascending);
	m_TrashedInvoiceListBox.addListSelectionListener(this);
	
	l_Icon = BillingModule.getButtonIcon ("trash_undelete_invoice.png");
	m_UndeleteButton = new JButton ();
	m_UndeleteButton.setIcon(l_Icon);
	m_UndeleteButton.setHorizontalAlignment(SwingConstants.LEFT);
	m_UndeleteButton.setVerticalTextPosition(AbstractButton.CENTER);
	m_UndeleteButton.setHorizontalTextPosition(AbstractButton.TRAILING); 
	m_UndeleteButton.setEnabled (BillingModule.userHasPermission(BillingModule.c_BillingModule, BillingModule.c_undeleteInvoices));
	m_UndeleteButton.addActionListener(this);
	
	m_ClearButton = new JButton ();
	m_ClearButton.setIcon(BillingModule.getButtonIcon("trash_empty.png"));
	m_ClearButton.setHorizontalAlignment(SwingConstants.LEFT);
	m_ClearButton.setVerticalTextPosition(AbstractButton.CENTER);
	m_ClearButton.setHorizontalTextPosition(AbstractButton.TRAILING); 
	m_ClearButton.setEnabled (BillingModule.userHasPermission(BillingModule.c_BillingModule, BillingModule.c_deleteInvoices));
	m_ClearButton.addActionListener(this);
	
	this.relocalize();
	
	this.add (m_TrashedInvoiceListBox, 	l_Constraints.xywh(2, 2, 1, 6));
	this.add (m_UndeleteButton, 		l_Constraints.xywh(4, 2, 1, 1));
	this.add (m_ClearButton, 			l_Constraints.xywh(4, 4, 1, 1));

	KeyStroke l_Stroke = KeyStroke.getKeyStroke("ESCAPE");
	
	this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(l_Stroke,"cancelSearch");
	this.getActionMap().put("cancelSearch", new SearchResetAction (this,"cancelSearch"));	
	
	m_InvoiceListeners = new InvoiceEventDispatcher ();
	}
	
//---------------------------------------------------------------------------
//***************************************************************************
//* Primitives                                       						*
//***************************************************************************
//---------------------------------------------------------------------------

public void addInvoiceListener(InvoiceListener p_Listener) 
	{
	m_InvoiceListeners.addInvoiceListener(p_Listener);
	}

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

public void removeInvoiceListener(InvoiceListener p_Listener) 
	{
	m_InvoiceListeners.removeInvoiceListener(p_Listener);
	}

//---------------------------------------------------------------------------
/**
* The private getInvoiceInterface returns an instance of the InvoiceBean
* session bean. On the first call, the InvoiceBean will actualy be looked up
* via JNDI. Once it has been found, the reference to the bean will be stored
* in a private data member. Doing so avoids JNDI lookups on later calls.
* @return an instance of the InvoiceBean session bean.
*/
//---------------------------------------------------------------------------

protected TrashedInvoiceStubInterface getTrashedInvoiceStubInterface ()
	{
	if (m_TrashedInvoiceStubInterface != null) return m_TrashedInvoiceStubInterface;

	try {
		m_TrashedInvoiceStubInterface = (TrashedInvoiceStubInterface) ManagerFactory.getRemote(TrashedInvoiceStubBean.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_TrashedInvoiceStubInterface;
	}

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

//private Collection <TrashedInvoiceStub> getInvoiceStubs ()
//	{
//	Collection <TrashedInvoiceStub>	l_InvoiceStubs;
//	
//	l_InvoiceStubs = m_TrashedInvoices.getTickedInvoiceStubs();
//	if ((l_InvoiceStubs == null) || (l_InvoiceStubs.size() == 0))
//		l_InvoiceStubs = m_TrashedInvoices.getTrashedInvoiceStubs();
//	
//	return l_InvoiceStubs;
//	}

//---------------------------------------------------------------------------
	
public void updateListing ()
	{
	TrashedInvoiceStubInterface		l_TrashedInvoiceStubInterface = null;
	Collection <TrashedInvoiceStub>	l_TrashedInvoiceStubs 		 = null;
	
	l_TrashedInvoiceStubInterface = this.getTrashedInvoiceStubInterface();
	
	if (l_TrashedInvoiceStubInterface == null) return;
	
	try {
		l_TrashedInvoiceStubs = l_TrashedInvoiceStubInterface.getAllTrashedInvoiceStubs();
		}
	catch (Exception p_Exception) 
		{
		m_Logger.warn(p_Exception.getLocalizedMessage());
		}
	
	if (l_TrashedInvoiceStubs != null) 
		{
		m_TrashedInvoices.setTrashedInvoiceStubs(l_TrashedInvoiceStubs);
		m_TrashedInvoiceListBox.packColumns();
		m_TrashedInvoiceListBox.validate();
		}
	}

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

public void searchTrashedInvoices (WhereClause p_Clause)
	{
	TrashedInvoiceStubInterface		l_TrashedInvoiceStubInterface = null;
	Collection <TrashedInvoiceStub>	l_TrashedInvoiceStubs;
	long							l_Time;
	long							l_Delta;
	
	l_TrashedInvoiceStubInterface = this.getTrashedInvoiceStubInterface();
	
	if (l_TrashedInvoiceStubInterface == null) return;
	
	try {
		l_Time = System.currentTimeMillis();
		
		l_TrashedInvoiceStubs = l_TrashedInvoiceStubInterface.getInvoiceStubsByWhereClause(p_Clause);
		
		l_Delta = System.currentTimeMillis() - l_Time;
		
		m_Logger.info("Search took " + l_Delta + " ms");
	
		m_TrashedInvoices.setTrashedInvoiceStubs(l_TrashedInvoiceStubs);
		
		l_Delta = System.currentTimeMillis() - l_Time;
		
		m_Logger.info("Total time required is " + l_Delta + " ms");
		}
	catch (Exception p_Exception) 
		{
		m_Logger.warn(p_Exception.getLocalizedMessage());
		}
	}


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

private void undeleteInvoice (TrashedInvoiceStub p_TrashedInvoiceStub)
	{
	TrashedInvoiceStubInterface		l_TrashedInvoiceStubInterface = null;
	Invoice							l_Invoice = null;
	InvoiceChangeEvent				l_Event;
	
	l_TrashedInvoiceStubInterface = this.getTrashedInvoiceStubInterface();
	
	if (l_TrashedInvoiceStubInterface == null) return;
	
	try {
		l_Invoice = l_TrashedInvoiceStubInterface.undeleteInvoice(p_TrashedInvoiceStub.getId());
		m_TrashedInvoices.removeTrashedInvoiceStub (p_TrashedInvoiceStub);
		m_TrashedInvoiceListBox.packColumns();
		m_TrashedInvoiceListBox.validate();
		}
	catch (Exception p_Exception) 
		{
		m_Logger.warn(p_Exception.getLocalizedMessage());
		}
	
	if (l_Invoice != null)
		{
		l_Invoice = BillingModule.getInstance().fetchLazyDependencies(l_Invoice);
		l_Event = new InvoiceChangeEvent (this, InvoiceChangeEvent.c_InvoiceUpdated,l_Invoice);
		m_InvoiceListeners.notifyInvoiceListeners(l_Event);
		}
	}

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

private void resetSearchCriteria ()
	{
	m_SearchHeader.resetTableHeaders();
	}

//---------------------------------------------------------------------------
//***************************************************************************
//* Search Worker                                    						*
//***************************************************************************
//---------------------------------------------------------------------------

Object SearchWorker (WhereClause p_Clause)
	{
	this.searchTrashedInvoices (p_Clause);
	return "";
	}

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

private void startSearchWorker (final WhereClause p_Clause)
	{
    m_SearchWorker = new SwingWorker() 
		{
		public Object construct() 
			{
			return SearchWorker (p_Clause);
			}
		public void start ()
			{
			MainFrame.getInstance().setWaitCursor(true);
			super.start();
			}
		public void finished ()
			{
			String []	l_Filler;
		
			m_TrashedInvoiceListBox.packColumns();
			m_TrashedInvoiceListBox.validate();
			MainFrame.getInstance().setWaitCursor(false);		
			
			l_Filler = new String [1];
			
			if (m_TrashedInvoices.getRowCount() > 1)
				{
				l_Filler[0] = Integer.valueOf(m_TrashedInvoices.getRowCount()).toString();
				MainFrame.getInstance().showMessage(Translatrix.getTranslationString("TrashPanel.InvoicesFound", l_Filler));
				}
			else if (m_TrashedInvoices.getRowCount() == 1)
				{
				MainFrame.getInstance().showMessage(Translatrix.getTranslationString("TrashPanel.InvoiceFound"));
				}
			else 
				{
				MainFrame.getInstance().showMessage(Translatrix.getTranslationString("TrashPanel.NoInvoicesFound"));
				}
			}
		public void interrupt ()
			{
			MainFrame.getInstance().setWaitCursor(false);
			super.interrupt();
			}
		};

	m_SearchWorker.start ();  
	}


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

public void preparetoShowup() 
	{
	this.updateListing();
	}

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

public void preparetoHide ()
	{
	}

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

public void relocalize() 
	{
	if (m_UndeleteButton != null)
		{
		m_UndeleteButton.setText (Translatrix.getTranslationString(c_UndeleteButton));
		m_UndeleteButton.setToolTipText (Translatrix.getTranslationString(c_UndeleteTip));
		
		m_ClearButton.setText (Translatrix.getTranslationString(c_ClearTrashButton));
		m_ClearButton.setToolTipText (Translatrix.getTranslationString(c_ClearTrashTip));
		}
	}

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

public void actionPerformed (ActionEvent p_Event) 
	{
	List<TrashedInvoiceStub>	l_SelectedIndices;
	AbstractTableModel			l_Model;
	
	
	
	if (p_Event.getSource().equals(m_UndeleteButton))
		{
		l_Model				= m_TrashedInvoiceListBox.getTableModel();
		l_SelectedIndices	= new LinkedList<TrashedInvoiceStub>();
		
		for (int index = 0; index < l_Model.getRowCount(); index++)
			{
			if ((Boolean)l_Model.getValueAt(index, 0))
				l_SelectedIndices.add(m_TrashedInvoices.getTrashedInvoiceStubAt(index));
			}
		
		for (TrashedInvoiceStub l_StubToUndelete : l_SelectedIndices)
			{
			this.undeleteInvoice(l_StubToUndelete);
			}
	}
	else if (p_Event.getSource().equals(m_ClearButton)) {
		this.trashClearDialog = new TrashClearDialog(this);
		
		if (this.trashClearDialog.showCenteredDialog() == GECAMedBaseDialog.OK_OPTION) {
			final Collection<TrashedInvoiceStub> stubs = this.trashClearDialog.getTrashedInvoices2Delete();
			if (stubs != null) {
				
				new Thread() {
					public void run() {
						TrashPanel.this.setWaitCursor(true);
						for (TrashedInvoiceStub trashedInvoiceStub : stubs) {
							try {
								getTrashedInvoiceStubInterface().deleteTrashedInvoiceByID(trashedInvoiceStub.getId());
								GECAMedLog.user(BillingModule.c_BillingModule, "DELETE TRASHED INVOICE", "Trashed Invoice with ID " + trashedInvoiceStub.getId() +  " has been deleted");
							} catch (Exception e) {
								m_Logger.log(Level.ERROR, "Error deleting trashed invoice: " + trashedInvoiceStub, e);
							}
						}
						updateListing();
						TrashPanel.this.setWaitCursor(false);
					}
				}.start();
				
				
			}
			
		}
		
	}
}

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

public void propertyChange (PropertyChangeEvent p_Event) 
	{
	Integer 			l_Column;
	String				l_ColumnName;
	Object				l_Criterion;
	WhereClause			l_Clause;
	HibernateCondition	l_Condition;

	if (p_Event.getSource().equals(m_SearchHeader))
		{
		l_Column     = (Integer) p_Event.getOldValue();
		l_Criterion  = 			p_Event.getNewValue();
		l_ColumnName = TrashedInvoiceStubListModel.c_TableHeaders[l_Column];
		
		if (l_Criterion == null) 
				{
				this.resetSearch();
				return;
				}
		
		if (SearchTableHeader.c_ExclusiveCriterionChanged.equals(p_Event.getPropertyName()))
			{		
			l_Clause    = new WhereClause ();

			if (TrashedInvoiceStubListModel.c_InvoiceNumberHeader.equals (l_ColumnName))
				{
				l_Condition = new HibernateCondition ("id",
													  HibernateOperator.c_EqualOperator,
													  (Integer)l_Criterion);
				l_Clause.addCondition(l_Condition);
				this.startSearchWorker(l_Clause);
				}
			else if (TrashedInvoiceStubListModel.c_InvoiceDateHeader.equals (l_ColumnName))
				{
				l_Condition = new HibernateCondition ("invoiceDate",
													  HibernateOperator.c_EqualOperator,
													  (Date)l_Criterion);
				l_Condition.setParameter("date");
				l_Clause.addCondition(l_Condition);
				this.startSearchWorker(l_Clause);
				}
			}
		}
	}

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

public void tableChanged(TableModelEvent p_Event) 
	{
	int					l_Row;
	int					l_RowCount;
	int					l_SelectedRows [];
	TrashedInvoiceStub	l_InvoiceStub;
	boolean				l_IsTicked;
	
	if (p_Event.getType() == TableModelEvent.UPDATE)
		{
		l_RowCount = m_TrashedInvoiceListBox.getTable().getSelectedRowCount();
		if (l_RowCount > 1)
			{
			m_TrashedInvoices.removeTableModelListener(this);
			
			l_Row = p_Event.getFirstRow();
			l_InvoiceStub = m_TrashedInvoices.getTrashedInvoiceStubAt(l_Row);
			l_IsTicked  = l_InvoiceStub.getTicked();
			l_SelectedRows = m_TrashedInvoiceListBox.getTable().getSelectedRows();	
			
			for (l_Row = 0; l_Row < l_SelectedRows.length; l_Row++)
				{
				l_InvoiceStub = m_TrashedInvoices.getTrashedInvoiceStubAt (l_SelectedRows[l_Row]);
				l_InvoiceStub.setTicked(l_IsTicked);
				m_TrashedInvoices.setTrashedInvoiceStubAt(l_InvoiceStub, l_SelectedRows[l_Row]);
				}
			
			m_TrashedInvoices.addTableModelListener(this);
			}
		}
	}

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

public void valueChanged (ListSelectionEvent p_Event) 
	{
//	if (!p_Event.getValueIsAdjusting())
//		{
//		this.reflectSelectionChange();
//		}
	}

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

public void resetSearch() 
	{
	this.resetSearchCriteria();
	this.updateListing();
	}

/**
 * sets the Mousecursor of the MainFrame to a WaitCursor and Back
 *
 * @param on true=waitcursor false=normalcursor
 */
public void setWaitCursor(boolean on) {
	if (on) {
		this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
	} else {
		this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
	}
}

///---------------------------------------------------------------------------
//***************************************************************************
//* End Of Class                                                            *
//***************************************************************************
}
