/*******************************************************************************
 * 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-2009
 *******************************************************************************/

package lu.tudor.santec.gecamed.core.gui.widgets;

import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;

import lu.tudor.santec.gecamed.core.gui.GECAMedColors;
import lu.tudor.santec.gecamed.core.gui.utils.GECAMedGuiUtils;
import lu.tudor.santec.i18n.Relocalizable;
import lu.tudor.santec.i18n.Translatrix;

import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;
import com.toedter.calendar.JDateChooser;

/**
 * This Panel consists of two date choosers, named FROM and UNTIL, commonly used to specify 
 * ranges of dates. Panel ensures that no matter how you specify dates, from date will
 * always be earliest of the two dates. Furthermore, configurable auto completion mode
 * assures that until date is setting depending on value of from date. 
 * 
 * @author Nico Mack nico.mack(at)tudor.lu
 */

public class FromUntilDatePanel extends JPanel implements PropertyChangeListener,
														  ItemListener,
														  Relocalizable
	{	
	private	int						m_AutoCompletionMode;
	
	private JLabel					m_FromDateLabel;
	private JDateChooser			m_FromDate;
	private JLabel					m_UntilDateLabel;
	private JCheckBox				m_UntilDateSwitch;
	private JDateChooser			m_UntilDate;
	
	private String					m_FromDateLabelKey;
	private String					m_UntilDateLabelKey;
	
//---------------------------------------------------------------------------
//***************************************************************************
//* Constants	                                                            *
//***************************************************************************
//---------------------------------------------------------------------------
	
	private static final long serialVersionUID = 1L;

	private static final String c_FromDateLabel  = "FromUntilDatePanel.FromDateLabel";
	private static final String c_UntilDateLabel = "FromUntilDatePanel.UntilDateLabel";
	
	public final static int	c_VerticalLayout   = 1;
	public final static int	c_HorizontalLayout = 2;
	
	public final static int c_AutoCompletionOff 	 = 0;
	public final static int c_UntilDateToLastOfMonth = 1;
	public final static int c_UntilDateToFromDate    = 2;
	
	private final static String c_VerticalLayoutColumns		=	"3dlu,fill:max(70dlu;pref):grow,3dlu";

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

//---------------------------------------------------------------------------
//***************************************************************************
//* Constructor	                                                            *
//***************************************************************************
//---------------------------------------------------------------------------
/**
 * Creates a new instance of FromUntilDatePanel
 */	
//---------------------------------------------------------------------------

public 	FromUntilDatePanel ()
	{
	m_AutoCompletionMode = c_AutoCompletionOff;
	this.buildPanel();
	}

//---------------------------------------------------------------------------
/**
 * Creates a new instance of FromUntilDatePanel
 * @param p_AutoCompletionMode specifies the auto compeletion mode to be
 * employed when setting From Date. Possible values are:
 * <ul>
 * <li>c_AutoCompletionOff : No auto completion</li>
 * <li>c_UntilDateToLastOfMonth : Set Until Date to last of month if
 * from date was set to first of month</li>
 * <li>c_UntilDateToFromDate : Set Until to same date as from date</li>
 * </ul>
 */	
//---------------------------------------------------------------------------

public 	FromUntilDatePanel (int p_AutoCompletionMode)
	{
	this.setAutoCompletionMode (p_AutoCompletionMode);
	this.buildPanel();
	}

//---------------------------------------------------------------------------
//***************************************************************************
//* Primitives	                                                            *
//***************************************************************************
//---------------------------------------------------------------------------
/**
 * The buildPanel creates and does the layout of the panel itself
 */
//---------------------------------------------------------------------------

private void buildPanel ()
	{
	CellConstraints	l_Constraints;
	FormLayout		l_Layout;
	
	l_Constraints  = new CellConstraints();
	
	l_Layout = new FormLayout(c_VerticalLayoutColumns, c_VerticalLayoutRows);
	this.setLayout (l_Layout);
	this.setOpaque(false);
		
	m_FromDateLabel = new JLabel (Translatrix.getTranslationString(c_FromDateLabel));
 	m_FromDateLabel.setForeground(GECAMedColors.c_LabelColor);
 	m_FromDate = GECAMedGuiUtils.getDateChooser(false);
	m_FromDate.addPropertyChangeListener(this);
	
	m_UntilDateLabel = new JLabel (Translatrix.getTranslationString(c_UntilDateLabel));
 	m_UntilDateLabel.setForeground(GECAMedColors.c_LabelColor);
 	m_UntilDateSwitch = new JCheckBox (Translatrix.getTranslationString(c_UntilDateLabel));
 	m_UntilDateSwitch.setForeground(GECAMedColors.c_LabelColor);
 	m_UntilDateSwitch.setBackground(GECAMedColors.c_GECAMedBackground);
 	m_UntilDateSwitch.addItemListener(this);
 	m_UntilDateSwitch.setSelected(false);
 	
 	m_UntilDate = GECAMedGuiUtils.getDateChooser(false);
 	m_UntilDate.setEnabled(m_UntilDateSwitch.isSelected());
 	
 	this.add (m_FromDateLabel, 	 l_Constraints.xywh(2, 2, 1, 1));
 	this.add (m_FromDate, 		 l_Constraints.xywh(2, 4, 1, 1));
 	this.add (m_UntilDateSwitch, l_Constraints.xywh(2, 6, 1, 1));
 	this.add (m_UntilDate, 		 l_Constraints.xywh(2, 8, 1, 1));
 	}

//---------------------------------------------------------------------------
/**
 * Returns the earliest of the two specified dates:
 * @param p_FirstDate specifies on of the dates
 * @param p_SecondDate specifies another one
 * @return the earliest in time of the two specified dates.
 */
//---------------------------------------------------------------------------

public static Date getEarliestDate (Date p_FirstDate, Date p_SecondDate)
	{
	Date l_EarliestDate = null;
	
	if ((p_FirstDate != null) && (p_SecondDate != null))
		{
		switch (p_FirstDate.compareTo(p_SecondDate))
			{
			case -1: l_EarliestDate = p_FirstDate;
					 break;
			case  0: l_EarliestDate = p_FirstDate;
				     break;
			case  1: l_EarliestDate = p_SecondDate;
					 break;
			}
		}
	else if ((p_FirstDate == null) && (p_SecondDate != null)) l_EarliestDate = p_SecondDate;
		else if ((p_FirstDate != null) && (p_SecondDate == null)) l_EarliestDate = p_FirstDate;
	
	return l_EarliestDate;
	}

//---------------------------------------------------------------------------
/**
 * Returns the latest of the two specified dates:
 * @param p_FirstDate specifies on of the dates
 * @param p_SecondDate specifies another one
 * @return the latest in time of the two specified dates.
 */
//---------------------------------------------------------------------------

public static Date getLatestDate (Date p_FirstDate, Date p_SecondDate)
	{
	Date l_LatestDate = null;
	
	if ((p_FirstDate != null) && (p_SecondDate != null))
		{
		switch (p_FirstDate.compareTo(p_SecondDate))
			{
			case -1: l_LatestDate = p_SecondDate;
					 break;
			case  0: l_LatestDate = p_SecondDate;
				     break;
			case  1: l_LatestDate = p_FirstDate;
					 break;
			}
		}
	else if ((p_FirstDate == null) && (p_SecondDate != null)) l_LatestDate = p_SecondDate;
		else if ((p_FirstDate != null) && (p_SecondDate == null)) l_LatestDate = p_FirstDate;
	
	return l_LatestDate;
	}
	
//---------------------------------------------------------------------------
/**
 * checkes whether specified date is first of month
 * @param p_Date specifies the date to check
 * @return <code>true</code> if specified date is first of month, <code>
 * false</code> otherwise
 */
//---------------------------------------------------------------------------

public static boolean isFirstOfMonth (Date p_Date)
	{
	GregorianCalendar	l_FirstOfMonth;
	
	l_FirstOfMonth = new GregorianCalendar();
	l_FirstOfMonth.setTime (p_Date);	
	return (l_FirstOfMonth.get (Calendar.DAY_OF_MONTH) == 1);
	}

//---------------------------------------------------------------------------
/**
 * checkes whether specified date is last of month
 * @param p_Date specifies the date to check
 * @return <code>true</code> if specified date is last of month, <code>
 * false</code> otherwise
 */
//---------------------------------------------------------------------------

public static Date lastOfMonth (Date p_Date)
	{
	GregorianCalendar	l_LastOfMonth;
	int					l_LastDay;
	
	l_LastOfMonth = new GregorianCalendar();
	l_LastOfMonth.setTime (p_Date);
	l_LastDay = l_LastOfMonth.getActualMaximum (Calendar.DAY_OF_MONTH);	
	l_LastOfMonth.set (Calendar.DAY_OF_MONTH, l_LastDay);

	return l_LastOfMonth.getTime();
	}

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

public void autoComplete ()
	{
	Date				l_StartDate;
	Date				l_EndDate;
	
	l_StartDate = this.getFromDate();
	l_EndDate   = this.getUntilDate();
		
	if (l_StartDate == null) return;
		
	switch (m_AutoCompletionMode)
		{
		case c_AutoCompletionOff : return;
			
		case c_UntilDateToLastOfMonth :
			
			if ((l_EndDate == null) && (isFirstOfMonth(l_StartDate)))
				l_EndDate = lastOfMonth (l_StartDate);
			break;
				
		case c_UntilDateToFromDate :
			
			if (l_EndDate == null) l_EndDate = l_StartDate;
			break;
		}
		
	m_FromDate.removePropertyChangeListener(this);
	m_UntilDate.removePropertyChangeListener(this);
		
	m_FromDate.setDate (l_StartDate);
	m_UntilDate.setDate (l_EndDate);
		
	m_FromDate.addPropertyChangeListener(this);
	m_UntilDate.addPropertyChangeListener(this);
	}

//---------------------------------------------------------------------------
//***************************************************************************
//* Class Body	                                                            *
//***************************************************************************
//---------------------------------------------------------------------------
/**
 * Sets the translatrix key for the FROM date label.
 * @param p_LabelKey specifies the translatrix key to be used when
 * relocalizing this panel. If not set, default key will be used.
 */
//---------------------------------------------------------------------------

public void setFromDateLabelKey (String p_LabelKey)
	{
	m_FromDateLabelKey = p_LabelKey;
	}

//---------------------------------------------------------------------------
/**
 * Sets the translatrix key for the UNTIL date label.
 * @param p_LabelKey specifies the translatrix key to be used when
 * relocalizing this panel. If not set, default key will be used.
 */
//---------------------------------------------------------------------------

public void setUntilDateLabelKey (String p_LabelKey)
	{
	m_UntilDateLabelKey = p_LabelKey;
	}

//---------------------------------------------------------------------------
/**
 * sets auto completion mode to the specified mode.
 * @param p_AutoCompletionMode specifies the auto compeletion mode to be
 * employed when setting From Date. Possible values are:
 * <ul>
 * <li>c_AutoCompletionOff : No auto completion</li>
 * <li>c_UntilDateToLastOfMonth : Set Until Date to last of month if
 * from date was set to first of month</li>
 * <li>c_UntilDateToFromDate : Set Until to same date as from date</li>
 * </ul>
 */	
//---------------------------------------------------------------------------

public void setAutoCompletionMode (int p_Mode)
	{
	if (	(p_Mode >= c_AutoCompletionOff)
		 && (p_Mode <= c_UntilDateToFromDate))
		{
		m_AutoCompletionMode = p_Mode;
		}
	else m_AutoCompletionMode = c_AutoCompletionOff;
	}

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

public void enableUntilDate (boolean p_EnableIt)
	{
	m_UntilDateSwitch.setSelected(p_EnableIt);
	}

//---------------------------------------------------------------------------
/**
 * Returns the from date. Method makes sure that returned from date always
 * is earliest of the two dates.
 * @return the from date, always the earliest of the two dates.
 */
//---------------------------------------------------------------------------

public Date getFromDate ()
	{
	Date 		l_FromDate;
	Date 		l_UntilDate;
	Calendar	l_EarliestMoment;
	
	l_FromDate  = m_FromDate.getDate();
	l_UntilDate = m_UntilDate.getDate();
	
	if ((l_FromDate != null) && (l_UntilDate != null))
		l_FromDate = getEarliestDate (l_FromDate, l_UntilDate);

	if (l_FromDate != null)
		{
		l_EarliestMoment = new GregorianCalendar();
		l_EarliestMoment.setTime(l_FromDate);
		l_EarliestMoment.set(Calendar.MILLISECOND, 0);
		l_EarliestMoment.set(Calendar.SECOND, 0);
		l_EarliestMoment.set(Calendar.MINUTE, 0);
		l_EarliestMoment.set(Calendar.HOUR_OF_DAY, 0);
		l_FromDate = l_EarliestMoment.getTime();
		}
		
	return l_FromDate;
	}

//---------------------------------------------------------------------------
/**
 * Sets the from date. In case until is empty, setting the from date will
 * trigger auto completion of until date, using specified auto completion
 * mode if set.
 * @param p_FromDate the new from date for this panel
 */
//---------------------------------------------------------------------------

public void setFromDate (Date p_FromDate)
	{
	m_FromDate.setDate(p_FromDate);
	}

//---------------------------------------------------------------------------
/**
 * Method allows to check whether or not the user ticked the until date
 * checkbox. If box isn't checked, until date can be ignored.
 * @return <code>true</code> if until date checkbox is ticked, <code>false</code>
 * otherwise.
 */
//---------------------------------------------------------------------------

public boolean getWithUntilDate ()
	{
	return m_UntilDateSwitch.isSelected();
	}

//---------------------------------------------------------------------------
/**
 * Presets the state of the until date checkbox
 */
//---------------------------------------------------------------------------

public void setWithUntilDate (boolean p_WithUntilDate)
	{
	m_UntilDateSwitch.setSelected(p_WithUntilDate);
	}

//---------------------------------------------------------------------------
/**
 * Returns the until date. Method makes sure that returned from date always
 * is the latest of the two dates.
 * @return the until date, always the latest of the two dates.
 */
//---------------------------------------------------------------------------

public Date getUntilDate ()
	{
	Date 		l_FromDate;
	Date 		l_UntilDate;
	Calendar	l_LatestMoment;
	
	l_FromDate  = m_FromDate.getDate();
	l_UntilDate = m_UntilDate.getDate();
	
	if ((l_FromDate != null) && (l_UntilDate != null))
		l_UntilDate = getLatestDate (l_FromDate, l_UntilDate);

	if (l_UntilDate != null)
		{
		l_LatestMoment = new GregorianCalendar();
		l_LatestMoment.setTime(l_UntilDate);
		l_LatestMoment.set(Calendar.MILLISECOND, 999);
		l_LatestMoment.set(Calendar.SECOND, 59);
		l_LatestMoment.set(Calendar.MINUTE, 59);
		l_LatestMoment.set(Calendar.HOUR_OF_DAY, 23);
		l_UntilDate = l_LatestMoment.getTime();
		}
		
	return l_UntilDate;
	}

//---------------------------------------------------------------------------
/**
 * Sets the until date.
 * @param p_FromDate the new until date for this panel
 */
//---------------------------------------------------------------------------

public void setUntilDate (Date p_UntilDate)
	{
	m_UntilDate.setDate(p_UntilDate);
	}

//---------------------------------------------------------------------------
/**
 * Mehtod is part of the PropertyChangeListener interface. Here, the method
 * listens to changes of the from date and depending on specified auto
 * completion mode will set until date.
 */
//---------------------------------------------------------------------------

public void propertyChange (PropertyChangeEvent p_Event) 
	{
	if (   (p_Event.getSource().equals(m_FromDate))
		&& ("date".equals(p_Event.getPropertyName()))
		&& (m_UntilDateSwitch.isSelected())) 
		{
		this.autoComplete();
		this.repaint();
		}
	}

//---------------------------------------------------------------------------
/**
 * Relocalizes all language dependent components of the panel
 */
//---------------------------------------------------------------------------

public void relocalize() 
	{
	m_FromDateLabelKey  = (m_FromDateLabelKey != null) ?m_FromDateLabelKey: c_FromDateLabel;
	m_UntilDateLabelKey = (m_UntilDateLabelKey != null)?m_UntilDateLabelKey:c_UntilDateLabel;
	
	if (m_FromDateLabel != null)	
		m_FromDateLabel.setText (Translatrix.getTranslationString(m_FromDateLabelKey));
	
//	if (m_FromDate != null) 
//		{
//		m_FromDate.setLocale (Translatrix.getLocale());
//		m_FromDate.setDateFormatString("d MMMM yyyy");   
//		m_FromDate.getDateEditor().setLocale (Translatrix.getLocale());
//		}
	
	if (m_UntilDateLabel != null)	
		m_UntilDateLabel.setText (Translatrix.getTranslationString(m_UntilDateLabelKey));
	
	if (m_UntilDateSwitch != null)	
		m_UntilDateSwitch.setText (Translatrix.getTranslationString(m_UntilDateLabelKey));
	
//	if (m_UntilDate != null) 
//		{
//		m_UntilDate.setLocale (Translatrix.getLocale());
//		m_UntilDate.setDateFormatString("d MMMM yyyy");   
//		m_UntilDate.getDateEditor().setLocale (Translatrix.getLocale());
//		}
	}

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

public void itemStateChanged (ItemEvent p_Event) 
	{
	if (p_Event.getSource().equals (m_UntilDateSwitch))
		{
		m_UntilDate.setEnabled(m_UntilDateSwitch.isSelected());
		if (m_UntilDateSwitch.isSelected()) {
			this.autoComplete();
		} else {
			m_UntilDate.setDate(null);
		}
		
		}
	}

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

}
