/*******************************************************************************
 * 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
 *******************************************************************************/
/*
 * Author: Johannes Hermen Tudor/Santec
 * Mail: johannes.hermen@tudor.lu
 * Created: Apr 6, 2005
 *
 */
package lu.tudor.santec.gecamed.core.gui.widgets;

import java.awt.BorderLayout;
import java.awt.Component;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.LinkedHashMap;

import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.SpinnerDateModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

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

import com.toedter.calendar.JDateChooser;

/**
 * JPanel with a Datechooser and a Time Editorfield and easy
 * Getter / Setter access  
 * 
 *
 * @Version
 * <br>$Log: DateTimeChooser.java,v $
 * <br>Revision 1.7  2008-09-25 09:43:06  heinemann
 * <br>fixed copyrights
 * <br>
 * <br>Revision 1.6  2008-01-15 09:29:38  hermen
 * <br>updated Javadoc and refactured code
 * <br>
 * <br>Revision 1.5  2007-12-06 14:46:44  hermen
 * <br>updated Javadoc and refactured code
 * <br>
 *
 */
public class DateTimeChooser extends JPanel implements ChangeListener, PropertyChangeListener
{
    //~ Instance fields ========================================================

	private static final long serialVersionUID = 1L;
	
	// type constants 
	public static final int DATE_TIME = 1;
	public static final int HOUR_BOX  = 2;
	public static final int DAY_BOX   = 3;
	
	/**
	 * property that is fired if the date/time changes
	 */
	public static final String TIME_CHANGED = "time_changed";

    private JDateChooser dateChooser;
    private JSpinner timeSpinner;
    private SpinnerDateModel timeModel;
    
    // set default type
    private int type = DATE_TIME;

	private JComboBox hourBox;

	private JComboBox weekdayBox;

	private LinkedHashMap<String, Integer> weekdayHash;

    //~ Constructors ===========================================================

    public DateTimeChooser() {
    	/* ================================================== */
    	this("HH:mm:ss", DATE_TIME);
		/* ================================================== */
    }
    
    /**
     * @param format format is not used in HOUR_BOX type
     * @param type
     */
    public DateTimeChooser(String format, int type) {
    	/* ================================================== */
    	this(format, type, 30);
		/* ================================================== */
    }
    
    /**
     * @param format
     * @param type
     * @param minOffset
     */
    public DateTimeChooser(String format, int type, int minOffset) {
    	/* ================================================== */
    	this.type = type;
    	if (DATE_TIME == type)
    		initDateTimeChooser(format);
    	if (HOUR_BOX == type)
    		initHourBox(minOffset);
    	if (DAY_BOX == type)
    		initDayBox();
		/* ================================================== */
    }
    
    
    
    /**
     * inits the combobox to choose a weekday
     */
    private void initDayBox() {
    	/* ================================================== */
    	this.setLayout(new BorderLayout());
    	
    	DateFormat weekdayFormat = new SimpleDateFormat("EEEE", Translatrix.getLocale());
		/* ------------------------------------------------------- */
    	// create the weekdays
    	Calendar c = new GregorianCalendar(Translatrix.getLocale());
		c.set(Calendar.DAY_OF_WEEK, c.getFirstDayOfWeek());
		
		this.weekdayHash = new LinkedHashMap<String, Integer>();
		
		for (int i = 0; i < 7; i++) {
			weekdayHash.put(weekdayFormat.format(c.getTime()), c.get(Calendar.DAY_OF_WEEK));
			c.roll(Calendar.DAY_OF_WEEK, true);
		}
		/* ------------------------------------------------------- */
		this.weekdayBox = new JComboBox(weekdayHash.keySet().toArray());
		/* ------------------------------------------------------- */
		
		/* ------------------------------------------------------- */
		try {
			weekdayBox.setSelectedIndex(0);
		} catch (Exception e) {
			e.printStackTrace();
		}
		/* ------------------------------------------------------- */
		this.add(weekdayBox, BorderLayout.CENTER);
    	
		/* ================================================== */ 
    }
    
    
    /**
     * init the hour combo box
     */
    private void initHourBox(int minOffset) {
		/* ====================================================== */
		this.setLayout(new BorderLayout());
		final DateFormat formater = DateFormat.getTimeInstance(DateFormat.SHORT, Translatrix.getLocale());
		/* ------------------------------------------------------- */
		this.hourBox = new JComboBox(DateTimeChooser.createDateList(30));
		/* ------------------------------------------------------- */
		this.hourBox.setRenderer(new DefaultListCellRenderer() {
		
			private static final long serialVersionUID = 1L;

			public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
				/* ====================================================== */
				if (value instanceof Date) {
					/* ------------------------------------------------------- */
					return super.getListCellRendererComponent(
							list, 
							formater.format(value),
							index, 
							isSelected,
							cellHasFocus);
					/* ------------------------------------------------------- */
				}
				else
					return super.getListCellRendererComponent(
							list, 
							value,
							index, 
							isSelected,
							cellHasFocus);
				/* ====================================================== */
			}
		});
		/* ------------------------------------------------------- */
		try {
			hourBox.setSelectedIndex(0);
		} catch (Exception e) {
			e.printStackTrace();
		}
		/* ------------------------------------------------------- */
		this.add(hourBox, BorderLayout.CENTER);
		/* ====================================================== */
	}

	/**
	 * @param format
	 */
	private void initDateTimeChooser(String format) {
    	/* ================================================== */
    	this.setLayout(new BorderLayout(5, 2));
//    	 contruct datechooser
    	this.dateChooser = GECAMedGuiUtils.getDateChooser(false);
    	
        // construct timechooser
        this.timeModel = new SpinnerDateModel();
        this.timeSpinner = new JSpinner(timeModel);
        JSpinner.DateEditor endeditor = new JSpinner.DateEditor(this.timeSpinner,
        		format);
        this.timeSpinner.setEditor(endeditor);

        // add to Panel
        this.add(this.dateChooser, BorderLayout.CENTER);
        this.add(this.timeSpinner, BorderLayout.EAST);


        // add listener
        this.timeModel.addChangeListener(this);
        this.dateChooser.addPropertyChangeListener(this);
		/* ================================================== */
    }
    
    
    //~ Methods ================================================================
    /**
     * @param date
     */
    public void setDate(Date date) {
    	if (date == null)
			return;
    	/* ------------------------------------------------------- */
    	if (DATE_TIME == type) {
    	/* ------------------------------------------------------- */
    		try {
    			this.dateChooser.setDate(date);
    			this.timeModel.setValue(date);
    		} catch (Exception e) {
    		}
    	/* ------------------------------------------------------- */
    	}
    	if (HOUR_BOX == type) {
    		/* ------------------------------------------------------- */
    		// try to find the next date
    		Date old = null;
    		
    		for (int i = 0; i < hourBox.getItemCount(); i++) {
    			/* ------------------------------------------------------- */
    			if (getTimeOfDay(date) >= getTimeOfDay((Date) hourBox.getItemAt(i)))
    				old = (Date) hourBox.getItemAt(i);
    			/* ------------------------------------------------------- */
    		}
    		if (old != null) {
    			/* ------------------------------------------------------- */
    			// old is the next smallest date
    			hourBox.setSelectedItem(old);
    			/* ------------------------------------------------------- */
    		}
    		else
    			try {
    				hourBox.setSelectedIndex(0);
				} catch (Exception e) {
					// bad luck
				}
			/* ------------------------------------------------------- */
    	}
    	if (DAY_BOX == type) {
    		/* ------------------------------------------------------- */
    		// get the day of week
    		Calendar cal = Calendar.getInstance();
    		cal.setTime(date);
    		int day = cal.get(Calendar.DAY_OF_WEEK);
    		for (String s : weekdayHash.keySet()) {
    			if (day == weekdayHash.get(s)) {
    				this.weekdayBox.setSelectedItem(s);
    				return;
    			}
    		}
    		try {
				weekdayBox.setSelectedIndex(0);
			} catch (Exception e) {
				// bad luck
			}
    		
    		/* ------------------------------------------------------- */
    	}
    }

    /**
     * @return
     */
    public Date getDate()
    {	
    	if (DATE_TIME == type) {
    		/* ------------------------------------------------------- */
    		Calendar date = new GregorianCalendar();
    		date.setTime(this.dateChooser.getDate());
    		Calendar time = new GregorianCalendar();
    		time.setTime(this.timeModel.getDate());
    		date.set(Calendar.HOUR_OF_DAY, time.get(Calendar.HOUR_OF_DAY));
    		date.set(Calendar.MINUTE, time.get(Calendar.MINUTE));
    		date.set(Calendar.SECOND, time.get(Calendar.SECOND));
    		return date.getTime();
    		/* ------------------------------------------------------- */
    	}
    	if (HOUR_BOX == type) {
    		/* ------------------------------------------------------- */
    		return (Date) hourBox.getSelectedItem();
    		/* ------------------------------------------------------- */
    	}
    	if (DAY_BOX == type) {
    		/* ------------------------------------------------------- */
    		String s =  (String) weekdayBox.getSelectedItem();
    		Calendar cal = Calendar.getInstance();
    		cal.set(Calendar.DAY_OF_WEEK, weekdayHash.get(s));
    		/* ------------------------------------------------------- */
    		return cal.getTime();
    		/* ------------------------------------------------------- */
    	}
    	
    	return null;
    }


    private void fireChange() {
    	/* ================================================== */
    	firePropertyChange(TIME_CHANGED, getDate(), new Date());
		/* ================================================== */
    }

    // time spinner
	public void stateChanged(ChangeEvent e) {
		/* ====================================================== */
		fireChange();
		/* ====================================================== */
	}
	// datechooser
	public void propertyChange(PropertyChangeEvent evt) {
		/* ====================================================== */
		if ("date".equals(evt.getPropertyName())) {
			fireChange();
		}
		/* ====================================================== */
	}
	
	
	private long getTimeOfDay(Date d) {
		/* ================================================== */
		Calendar c = Calendar.getInstance();
		c.setTime(d);
		long r = c.get(Calendar.HOUR_OF_DAY) * 60;
		r = r + c.get(Calendar.MINUTE);
		
		return r;
		/* ================================================== */
	}
	
	
	/**
	 * Creates an array of dates in distance of the minutes offset
	 * @param minOffset
	 * @return
	 */
	public static Date[] createDateList(int minOffset) {
		/* ================================================== */
		if (minOffset < 0)
			minOffset = 0;
		if (minOffset > 60)
			minOffset = 60;
		/* ------------------------------------------------------- */
		try {
			/* ------------------------------------------------------- */
			Calendar cal = Calendar.getInstance();
			cal.set(Calendar.DAY_OF_MONTH, 0);
			cal.set(Calendar.MONTH, 0);
			cal.set(Calendar.HOUR_OF_DAY, 0);
			cal.set(Calendar.MINUTE, 0);
			cal.set(Calendar.SECOND, 0);
			cal.set(Calendar.MILLISECOND, 0);
			
			int capacity = 60*24 / minOffset;
			
			Date[] dates = new Date[capacity];

			int limit = 60 / minOffset;
			
			int count = 1;
			for (int i = 0; i < capacity; i++) {
				/* ------------------------------------------------------- */
				dates[i] = cal.getTime();
				
				cal.roll(Calendar.MINUTE, minOffset);
				if (count == limit) {
					cal.roll(Calendar.HOUR_OF_DAY, 1);
					count = 0;
				}
				count++;
				
				/* ------------------------------------------------------- */
			}
			return dates;
			/* ------------------------------------------------------- */
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		/* ================================================== */
	}
	
}
