/*******************************************************************************
 * 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.addressbook.gui.list;

import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;

import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import lu.tudor.santec.gecamed.addressbook.ejb.entity.beans.Contact;
import lu.tudor.santec.gecamed.addressbook.ejb.session.beans.AddressbookManager;
import lu.tudor.santec.gecamed.addressbook.ejb.session.interfaces.AddressbookInterface;
import lu.tudor.santec.gecamed.addressbook.gui.AddressbookModule;
import lu.tudor.santec.gecamed.addressbook.gui.ContactEditDialog;
import lu.tudor.santec.gecamed.addressbook.vcard.VCardFileFilter;
import lu.tudor.santec.gecamed.addressbook.vcard.VCardImportDialog;
import lu.tudor.santec.gecamed.addressbook.vcard.VCardManager;
import lu.tudor.santec.gecamed.billing.gui.medpres.MedPres;
import lu.tudor.santec.gecamed.core.gui.GECAMedMessage;
import lu.tudor.santec.gecamed.core.gui.GECAMedModule;
import lu.tudor.santec.gecamed.core.gui.IconFetcher;
import lu.tudor.santec.gecamed.core.gui.MainFrame;
import lu.tudor.santec.gecamed.core.gui.utils.AutoCompletion;
import lu.tudor.santec.gecamed.core.utils.ManagerFactory;
import lu.tudor.santec.i18n.Translatrix;
import lu.tudor.santec.widgets.gui.ButtonFactory;

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

/**
 * JPanel that shows a list of the Addressbooks contacts with a preview 
 * on the right side.
 * 
 *
 * @author Johannes Hermen johannes.hermen(at)tudor.lu
 *
 * @version
 * <br>$Log: ContactTablePanel.java,v $
 * <br>Revision 1.26  2012-11-26 07:56:15  ferring
 * <br>New placeholders are introduced to the letter module
 * <br>The replacement algorithm in Word- and WriterCotnroller has been changed
 * <br>Multicontacts are prepared, but not yet implemented
 * <br>
 * <br>Revision 1.25  2010-08-12 08:01:48  hermen
 * <br>fixed #595: Full contact list reload after single contact modification
 * <br>
 * <br>Revision 1.24  2010-04-23 13:15:00  hermen
 * <br>small improvements
 * <br>
 * <br>Revision 1.23  2010-04-23 05:59:09  hermen
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.22  2010-04-15 09:36:06  hermen
 * <br>added delete/export of multiple contacts
 * <br>
 * <br>Revision 1.21  2009-05-11 10:01:42  hermen
 * <br>reload type list if contact is added/edited
 * <br>
 * <br>Revision 1.20  2008-12-05 17:34:23  heinemann
 * <br>moved some basic classes to lu.tudor.santec.widgets
 * <br>
 * <br>Revision 1.19  2008-09-25 09:42:27  heinemann
 * <br>fixed copyrights
 * <br>
 * <br>Revision 1.18  2008-06-02 12:32:48  hermen
 * <br>small-screen gui improvement
 * <br>
 * <br>Revision 1.17  2008-05-28 12:42:23  hermen
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.16  2008-05-13 10:12:44  hermen
 * <br>updated searchdialog
 * <br>
 * <br>Revision 1.15  2008-05-13 09:51:25  hermen
 * <br>added contact search dialog
 * <br>
 * <br>Revision 1.14  2008-05-09 13:40:33  hermen
 * <br>select first entry
 * <br>
 * <br>Revision 1.13  2008-05-08 14:04:41  heinemann
 * <br>*** empty log message ***
 * <br>
 * <br>Revision 1.12  2008-05-08 09:03:14  hermen
 * <br>changed formatting
 * <br>
 * <br>Revision 1.11  2008-05-08 09:02:17  hermen
 * <br>changed formatting
 * <br>
 * <br>Revision 1.10  2008-05-07 13:25:23  hermen
 * <br>updated import
 * <br>
 * <br>Revision 1.9  2008-05-06 14:29:13  hermen
 * <br>updated javadoc
 * <br>
 *
 */
public class ContactTablePanel extends JPanel
{
	
	private static final long			serialVersionUID	= 1L;
	
	private static final int			SEARCH_LIMIT		= 10000;
	
	private JTable						table;
	private ContactTableModel			contactTableModel;
	public AddressbookInterface			addressManager;
	
	private MouseEvent					event;
	
	private JPopupMenu					popup;
	
	public ContactEditDialog			contactEditDialog;
	
	private Vector<IContactListener>	contactListeners	= new Vector<IContactListener>();
	
	private JTextField					searchTextField;
	
	private JTextField					searchAddressPhoneField;
	
	private JButton						clearButton;
	
	private VCardImportDialog			importDialog		= new VCardImportDialog();
	
	private VCardFileFilter				fileFilter			= new VCardFileFilter();
	
	private JComboBox					type;
	
	private boolean						listenersActivated;
	
	private JScrollPane					jsp;
	
	
	/**
	 * creates the panel
	 */
	public ContactTablePanel()
	{
		this.setOpaque(false);
		this.setLayout(new BorderLayout());
		
		addressManager = (AddressbookInterface) ManagerFactory.getRemote(AddressbookManager.class);
		
		contactEditDialog = new ContactEditDialog();
		
		this.contactTableModel = new ContactTableModel();
		this.table = new JTable(contactTableModel);
		this.table.getTableHeader().setReorderingAllowed(false);
		this.table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
		this.table.setColumnSelectionAllowed(false);
		
		ContactListRenderer ren = new ContactListRenderer(this.table, this.contactTableModel);
		
		this.table.getColumnModel().getColumn(0).setCellRenderer(ren);
		this.table.getColumnModel().getColumn(0).setMinWidth(150);
		this.table.getColumnModel().getColumn(1).setCellRenderer(ren);
		this.table.getColumnModel().getColumn(1).setMinWidth(150);
		this.table.getColumnModel().getColumn(2).setCellRenderer(ren);
		this.table.getColumnModel().getColumn(2).setMinWidth(140);
		this.table.getColumnModel().getColumn(2).setMaxWidth(140);
		this.table.getColumnModel().getColumn(3).setCellRenderer(ren);
		this.table.getColumnModel().getColumn(3).setMaxWidth(80);
		this.table.getColumnModel().getColumn(4).setCellRenderer(ren);
		this.table.getColumnModel().getColumn(4).setMaxWidth(80);
		this.table.addMouseListener(new MouseAdapter()
		{
			
			public void mouseClicked(MouseEvent e)
			{
				/* ============================================= */
				
				// doubleclick --> open
				if (e.getClickCount() >= 2)
				{
					// get the current row
					int row = table.rowAtPoint(e.getPoint());
					try
					{
						Contact contact = contactTableModel.getContactforRow(row);
						for (Iterator<IContactListener> iter = contactListeners.iterator(); iter.hasNext();)
						{
							IContactListener listener = (IContactListener) iter.next();
							listener.contactClicked(contact);
						}
					}
					catch (Exception ee)
					{
						MainFrame.reportServerError(ee);
					}
				}
				/* ============================================= */
			}
			
			
			/* (non-Javadoc)
			 * @see java.awt.event.MouseAdapter#mouseReleased(java.awt.event.MouseEvent)
			 */
			@Override
			public void mouseReleased(MouseEvent e)
			{
				if (e.isPopupTrigger())
				{
					showMenu(e);
				}
			}
			
			
			/* (non-Javadoc)
			 * @see java.awt.event.MouseAdapter#mousePressed(java.awt.event.MouseEvent)
			 */
			@Override
			public void mousePressed(MouseEvent e)
			{
				if (e.isPopupTrigger())
				{
					showMenu(e);
				}
			}
		});
		
		this.table.getSelectionModel().addListSelectionListener(new ListSelectionListener()
		{
			public void valueChanged(ListSelectionEvent e)
			{
				if (e.getValueIsAdjusting() == false)
				{
					Contact contact = contactTableModel.getContactforRow(table.getSelectedRow());
					for (Iterator<IContactListener> iter = contactListeners.iterator(); iter.hasNext();)
					{
						IContactListener listener = (IContactListener) iter.next();
						listener.contactChanged(contact);
					}
				}
			}
		});
		
		this.table.setOpaque(false);
		jsp = new JScrollPane(this.table);
		jsp.setOpaque(false);
		jsp.getViewport().setOpaque(false);
		
		this.add(jsp, BorderLayout.CENTER);
		
		KeyListener searchFieldKeyListener = new KeyListener()
		{
			private Timer	timer;
			
			
			public void keyTyped(KeyEvent e)
			{
				if (e.getKeyCode() == KeyEvent.VK_TAB)
				{
					try
					{
						((JComponent) e.getSource()).transferFocus();
					}
					catch (Exception ee)
					{
					}
				}
			}
			
			
			public void keyPressed(KeyEvent e)
			{
				
			}
			
			
			public void keyReleased(KeyEvent e)
			{
				// select next contact
				if (e.getKeyCode() == KeyEvent.VK_DOWN)
				{
					try
					{
						int row = table.getSelectedRow();
						table.setRowSelectionInterval(row + 1, row + 1);
					}
					catch (Exception ee)
					{
					}
					// select prev contact
				}
				else if (e.getKeyCode() == KeyEvent.VK_UP)
				{
					try
					{
						int row = table.getSelectedRow();
						table.setRowSelectionInterval(row - 1, row - 1);
					}
					catch (Exception ee)
					{
					}
					// open selected contact
				}
				else if (e.getKeyCode() == KeyEvent.VK_ENTER)
				{
					//				loadPatient((Boolean) patientListModule.patientListSettingsPlugin.getValue(PatientSearchSettingsPlugin.OPEN_PATIENT_IN_NEW_TAB));
					// start new search
				}
				else if (listenersActivated)
				{
					if (timer != null)
					{
						timer.cancel();
					}
					timer = new Timer();
					timer.schedule(new SearchTask(), 300);
				}
			}
		};
		
		// searchfield for name, ssn 
		this.searchTextField = new JTextField(30);
		this.searchTextField.addKeyListener(searchFieldKeyListener);
		
		// searchfield for phone, address 
		this.searchAddressPhoneField = new JTextField(15);
		this.searchAddressPhoneField.addKeyListener(searchFieldKeyListener);
		
		// searchbox for type
		this.type = new JComboBox();
		this.type.addKeyListener(searchFieldKeyListener);
		this.type.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				if (listenersActivated)
				{
					reload();
				}
			}
		});
		Dimension d = searchAddressPhoneField.getPreferredSize();
		type.setPreferredSize(d);
		
		AutoCompletion.enableWithFreeText(this.type);
		
		
		this.clearButton = ButtonFactory.createEffectButton("", GECAMedModule.getMediumIcon(GECAMedModule.EDIT_CLEAR_LTR));
		this.clearButton.addActionListener(new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				searchTextField.setText("");
				searchAddressPhoneField.setText("");
				setFocus();
				reload();
			}
		});
	}
	
	
	/**
	 * fires a search with the String from the search fields and updates the tablemodel
	 * @param nameSearch
	 * @param addressSearch
	 * @param type
	 */
	private void findContacts(String nameSearch, String addressSearch, String type)
	{
		try
		{
			contactTableModel.setContacts(addressManager.findContacts(nameSearch, addressSearch, type, SEARCH_LIMIT));
			try
			{
				this.table.setRowSelectionInterval(0, 0);
			}
			catch (Exception e)
			{
			}
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}
	
	
	/**
	 * reloads the tablemodel with the current search words
	 */
	public void reload()
	{
		new Thread()
		{
			public void run()
			{
				setWaitCursor(true);
				int row = table.getSelectedRow();
				findContacts(searchTextField.getText(), searchAddressPhoneField.getText(), (String) type.getSelectedItem());
				try
				{
					table.setRowSelectionInterval(row, row);
				}
				catch (Exception e)
				{
				}
				setWaitCursor(false);
			}
		}.start();
	}
	
	
	/**
	 * show a menu on right click
	* @param c
	 * @param x
	 * @param y
	 */
	private void showMenu(MouseEvent e)
	{
		this.event = e;
		if (popup == null)
		{
			popup = new JPopupMenu();
			popup.add(new AbstractAction(Translatrix.getTranslationString("Addressbook.deleteContact"), IconFetcher.getSmallIcon(AddressbookModule.class, AddressbookModule.ICON_DELETE_CONTACT))
			{
				private static final long	serialVersionUID	= 1L;
				
				
				public void actionPerformed(ActionEvent ae)
				{
					//	get the current rows
					int[] selectedRows = table.getSelectedRows();
					final Vector<Contact> selectedContacts = new Vector<Contact>();
					StringBuffer sb = new StringBuffer();
					if (selectedRows != null)
					{
						for (int i = 0; i < selectedRows.length; i++)
						{
							Contact c = contactTableModel.getContactforRow(selectedRows[i]);
							selectedContacts.add(c);
							if (i < 10)
							{
								sb.append(c.toNameString()).append("\n");
							}
							else if (i == 10)
							{
								sb.append(".....").append("\n");
							}
							
						}
					}
					
					try
					{
						String[] qArr = { sb.toString(), selectedRows.length+"" };
						int n = JOptionPane.showConfirmDialog(MainFrame.getInstance(), Translatrix.getTranslationString("Addressbook.deleteContactQuestion", qArr), Translatrix.getTranslationString("Addressbook.deleteContact"), JOptionPane.YES_NO_OPTION);
						if (n == JOptionPane.YES_OPTION)
						{
							new Thread()
							{
								public void run()
								{
									MainFrame.getInstance().setWaitCursor(true);
									for (Contact c : selectedContacts)
									{
										addressManager.deleteContact(c);
									}
									MainFrame.fireGECAMedMessage (new GECAMedMessage(AddressbookModule.getInstance(), MedPres.MEDPRES_CHANGED, null, null));
									fillTypeBox();
									reload();
									MainFrame.getInstance().setWaitCursor(false);
								}
							}.start();
						}
					}
					catch (Exception e)
					{
						e.printStackTrace();
					}
				}
			});
			popup.add(new AbstractAction(Translatrix.getTranslationString("Addressbook.exportContact"), IconFetcher.getSmallIcon(AddressbookModule.class, AddressbookModule.ICON_EXPORT_CONTACT))
			{
				private static final long	serialVersionUID	= 1L;
				
				
				public void actionPerformed(ActionEvent ae)
				{
					//	get the current rows
					final Vector<Contact> selectedContacts = new Vector<Contact>();
					final int[] selectedRows = table.getSelectedRows();
					if (selectedRows != null)
					{
						new Thread()
						{
							public void run()
							{
								MainFrame.getInstance().setWaitCursor(true);
								for (int i = 0; i < selectedRows.length; i++)
								{
									Contact c = contactTableModel.getContactforRow(selectedRows[i]);
									selectedContacts.add(c);
								}
								try
								{
									exportContacts(selectedContacts);
								}
								catch (Exception e)
								{
									e.printStackTrace();
								}
								MainFrame.getInstance().setWaitCursor(false);
							}
						}.start();
					}
					
				}
			});
		}
		int row = table.rowAtPoint(event.getPoint());
		if (row < 0)
			return;
		
		int[] selectedRows = table.getSelectedRows();
		if (selectedRows == null || selectedRows.length == 0)
		{
			table.setRowSelectionInterval(row, row);
		}
		popup.show(e.getComponent(), e.getX(), e.getY());
	}
	
	
	/**
	 * creates a new Contact by showing a Dialog
	 */
	public void newContact()
	{
		if (contactEditDialog.showDialog(new Contact()))
		{
			Contact contact = contactEditDialog.getContact();
			addressManager.saveContact(contact);
			MainFrame.fireGECAMedMessage (new GECAMedMessage(AddressbookModule.getInstance(), MedPres.MEDPRES_CHANGED, null, null));
			fillTypeBox();
			reload();
		}
	}
	
	
	/**
	 * @param listener
	 */
	public void addContactListener(IContactListener listener)
	{
		this.contactListeners.add(listener);
	}
	
	
	/**
	 * returns the Searchbar with teh searchfields
	 * @return
	 */
	public JComponent getSearchComponent()
	{
		CellConstraints cc = new CellConstraints();
		JPanel jp = new JPanel(new FormLayout("pref, pref:grow, 2dlu, 90dlu, 2dlu, 60dlu", "pref, pref"));
		jp.setOpaque(false);
		jp.add(this.clearButton, cc.xy(1, 2));
		JLabel l2 = new JLabel(Translatrix.getTranslationString("Addressbook.name"));
		l2.setOpaque(false);
		jp.add(l2, cc.xy(2, 1));
		jp.add(this.searchTextField, cc.xy(2, 2));
		JLabel l3 = new JLabel(Translatrix.getTranslationString("Addressbook.addressesphone"));
		l3.setOpaque(false);
		jp.add(l3, cc.xy(4, 1));
		jp.add(this.searchAddressPhoneField, cc.xy(4, 2));
		JLabel l4 = new JLabel(Translatrix.getTranslationString("Addressbook.type"));
		l4.setOpaque(false);
		jp.add(l4, cc.xy(6, 1));
		jp.add(this.type, cc.xy(6, 2));
		return jp;
	}
	
	
	/**
	 * set the focus to the searchfield 
	 */
	public void setFocus()
	{
		this.searchTextField.selectAll();
		this.searchTextField.requestFocus();
		this.fillTypeBox();
	}
	
	class SearchTask extends TimerTask
	{
		@Override
		public void run()
		{
			reload();
		}
	}
	
	
	/**
	 * imports contacts from a vcard file
	 */
	public void importContacts()
	{
		final JFileChooser jfc = MainFrame.getFileChooser();
		jfc.addChoosableFileFilter(fileFilter);
		if (jfc.showOpenDialog(MainFrame.getInstance()) == JFileChooser.APPROVE_OPTION)
		{
			final Collection<Contact> contacts = VCardManager.readVCard(jfc.getSelectedFile(), this);
			
			final String type = importDialog.showDialog(contacts, loadTypes());
			if (type == null)
				return;
			
			new Thread()
			{
				public void run()
				{
					MainFrame.getInstance().setWaitCursor(true);
					for (Iterator<Contact> iter = contacts.iterator(); iter.hasNext();)
					{
						Contact contact = (Contact) iter.next();
						try
						{
							contact.setContactType(type);
							addressManager.saveContact(contact);
						}
						catch (Exception e)
						{
							System.out.println(contact);
							e.printStackTrace();
						}
					}
					jfc.removeChoosableFileFilter(fileFilter);
					try
					{
						SwingUtilities.invokeAndWait(new Runnable()
						{
							public void run()
							{
								fillTypeBox();
								reload();
							}
						});
					}
					catch (Exception e)
					{
						e.printStackTrace();
					}
					MainFrame.getInstance().setWaitCursor(false);
					MainFrame.fireGECAMedMessage (new GECAMedMessage(AddressbookModule.getInstance(), MedPres.MEDPRES_CHANGED, null, null));
				}
			}.start();
		}
	}
	
	
	/**
	 * exports a contact to a vcard file
	 * @param contact
	 */
	public void exportContacts(Vector<Contact> contacts)
	{
		JFileChooser jfc = MainFrame.getFileChooser();
		jfc.setSelectedFile(new File(contacts.get(0).toNameString().trim() + ".vcf"));
		if (jfc.showSaveDialog(MainFrame.getInstance()) == JFileChooser.APPROVE_OPTION)
		{
			StringBuffer sb = new StringBuffer();
			
			for (Contact c : contacts)
			{
				String vCard = VCardManager.createVCard(c);
				sb.append(vCard);
			}
			
			try
			{
				BufferedWriter br = new BufferedWriter(new FileWriter(jfc.getSelectedFile()));
				br.write(sb.toString());
				br.close();
			}
			catch (IOException e)
			{
				e.printStackTrace();
			}
		}
		jfc.setSelectedFile(null);
		
	}
	
	
	private String[] loadTypes()
	{
		Collection<String> types = addressManager.getContactTypes();
		String[] typeArr = new String[types.size() + 1];
		int i = 1;
		typeArr[0] = "";
		for (Iterator<String> iter = types.iterator(); iter.hasNext();)
		{
			String element = (String) iter.next();
			typeArr[i] = element;
			i++;
		}
		return typeArr;
	}
	
	
	public void fillTypeBox()
	{
		listenersActivated = false;
		Object item = type.getSelectedItem();
		String[] types = loadTypes();
		type.removeAllItems();
		for (int i = 0; i < types.length; i++)
		{
			type.addItem(types[i]);
		}
		try
		{
			if (item != null)
				type.setSelectedItem(item);
			else
				type.setSelectedIndex(0);
		}
		catch (Exception e)
		{
			type.setSelectedIndex(0);
		}
		listenersActivated = true;
	}
	
	
	public void setSearch(String search, String type)
	{
		listenersActivated = false;
		this.searchTextField.setText(search);
		if (type != null && !"".equals(type))
		{
			this.type.setSelectedItem(type);
		}
		else
		{
			this.type.setSelectedIndex(0);
		}
		listenersActivated = true;
		this.reload();
	}
	
	
	public List<Contact> getContacts()
	{
		int[]			selection	= table.getSelectedRows();
		List<Contact>	contacts	= new ArrayList<Contact>(selection.length);
		
		
		for (int row : selection)
		{
			contacts.add(contactTableModel.getContactforRow(row));
		}
		
		
		
		return contacts;
	}
	
	
	/**
	 * sets the Mousecursor of the MainFrame to a WaitCursor and Back
	 *
	 * @param on true=waitcursor false=normalcursor
	 */
	public void setWaitCursor(boolean on)
	{
		Cursor c = null;
		if (on)
		{
			c = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
		}
		else
		{
			c = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
		}
		this.table.setCursor(c);
		this.jsp.setCursor(c);
		this.setCursor(c);
	}
	
	
	public void updateContact(Contact contact)
	{
		this.contactTableModel.updateContact(contact);
	}
	
	
	public void setSelectionMode (int selectionMode)
	{
		this.table.setSelectionMode(selectionMode);
	}
}
