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

import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.font.TextAttribute;
import java.awt.image.BufferedImage;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Vector;

import javax.swing.GrayFilter;
import javax.swing.ImageIcon;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;

import lu.tudor.santec.gecamed.core.ejb.entity.beans.GECAMedEntityBean;
import lu.tudor.santec.gecamed.core.gui.GECAMedLists;
import lu.tudor.santec.gecamed.core.gui.MainFrame;
import lu.tudor.santec.gecamed.core.utils.AutoTranslatable;
import lu.tudor.santec.gecamed.formeditor.gui.model.ComboBoxElement;
import lu.tudor.santec.i18n.Translatrix;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import com.toedter.calendar.JDateChooser;
import com.toedter.calendar.JSpinnerDateEditor;
import com.toedter.calendar.JTextFieldDateEditor;

/**
 * @author jens.ferring(at)tudor.lu
 * 
 * @version
 * <br>$Log: GECAMedGuiUtils.java,v $
 * <br>Revision 1.1  2013-11-28 10:38:26  ferring
 * <br>GECAMedUtils split into utils and GUI utils classes
 * <br>
 * <br>Revision 1.8  2013-11-27 10:08:52  ferring
 * <br>Default date format strings added
 * <br>
 * <br>Revision 1.7  2013-11-22 14:43:22  ferring
 * <br>indexOf item in collection method found
 * <br>
 * <br>Revision 1.6  2013-11-14 16:51:06  ferring
 * <br>Method added to calculate the length of a string
 * <br>
 * <br>Revision 1.5  2013-10-30 09:48:33  troth
 * <br>Fix the range size of the if ;)
 * <br>
 * <br>Revision 1.4  2013-10-29 13:31:54  ferring
 * <br>date formating methods added
 * <br>
 * <br>Revision 1.3  2013-10-28 16:52:59  ferring
 * <br>os notification changed and shortcut key modifier added
 * <br>
 * <br>Revision 1.2  2013-09-04 06:19:16  ferring
 * <br>eSante view bugfixes
 * <br>
 * <br>Revision 1.1  2013-07-15 06:18:34  ferring
 * <br>logging changed
 * <br>
 */

public class GECAMedGuiUtils
{
	/* ======================================== */
	// MEMBERS
	/* ======================================== */
	
	/** the logger Object for this class */
	private static Logger logger = Logger.getLogger(GECAMedGuiUtils.class.getName());
	
	public static final String DATEFORMAT_FRENCH_SHORT = "dd/MM/yyyy";
	public static final String DATEFORMAT_GERMAN_SHORT = "dd.MM.yyyy";
	public static final String DATEFORMAT_ENGLISH_SHORT = "dd/MM/yyyy";

	private static final String TIMEFORMAT = "HH:mm";
	private static final String TIMEFORMAT_LONG = "HH:mm:ss";
	
	/* ======================================== */
	// CLASS BODY
	/* ======================================== */
	
	/**
	 * 
	 * 
	 * @param c The JComponent to change the font of
	 * @param attributesAndValues the attributes and its values alternating order
	 */
	@SuppressWarnings("unchecked")
	public static void changeFont (JComponent c, Object ... attributesAndValues)
	{
		Map<TextAttribute, Object> attributes = (Map<TextAttribute, Object>) c.getFont().getAttributes();
		int	index	= 0;
		
		while (index < attributesAndValues.length)
		{
			try
			{
				attributes.put((TextAttribute) attributesAndValues[index++], 
						attributesAndValues[index++]);
			}
			catch (ClassCastException e)
			{
				logger.log(Level.WARN, e.getMessage());
				return;
			}
			catch (ArrayIndexOutOfBoundsException e)
			{
				logger.log(Level.WARN, "Number of attributes and values to change the font must be evan, but is " + 
						attributesAndValues.length + 
						".\nAn alternating order of java.awt.font.TextAttribute and its value is needed.");
				return;
			}
		}
		
		c.setFont(new Font(attributes));
	}
	
	
	/**
	 * NOTE: This method does NOT work for HTML content.
	 * 
	 * @param label The label to calculate the needed length for
	 * @return The length of the String of this label.
	 */
	public static int getStringSize (JLabel label)
	{
		return getStringSize(label.getFont(), label.getText());
	}
	
	
	public static int getStringSize (Font font, String s)
	{
		BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
		FontMetrics fm = img.getGraphics().getFontMetrics(font);
		return fm.stringWidth(s);
	}
	
	
	public static boolean isShortCutDown (InputEvent e)
	{
		return (e.getModifiersEx() & MainFrame.SHORTCUT_KEY) != 0;
	}

	
	public static <T extends GECAMedEntityBean & AutoTranslatable> Vector<ComboBoxElement<T>> getTranslatedList (Class<T> clazz)
	{
		return getTranslatedList(clazz, true);
	}
	
	
	public static <T extends GECAMedEntityBean & AutoTranslatable> Vector<ComboBoxElement<T>> getTranslatedList (Class<T> clazz, boolean orderedByTranslation)
	{
		if (clazz == null)
			throw new RuntimeException("The class parameter must not be null");
		
		Vector<ComboBoxElement<T>> resultList = new Vector<ComboBoxElement<T>>();
		List<T> beanList = GECAMedLists.getListReference(clazz);
		String translation;
		
		for (T t : beanList)
		{
			translation = Translatrix.getTranslationString(t.getTranslationKey());
			if (translation == null || translation.equals(t.getTranslationKey()))
				translation = t.getDefaultTranslation();
			
			resultList.add(new ComboBoxElement<T>(translation, t));
		}
		
		if (orderedByTranslation)
			Collections.sort(resultList);
		
		return resultList;
	}
	
	
	public static Vector<ComboBoxElement<String>> getTranslatedList (Collection<String> keys, String translationPrefix, boolean orderedByTranslation)
	{
		Vector<ComboBoxElement<String>> resultList = new Vector<ComboBoxElement<String>>();
		String translation;
		String translationKey;
		
		for (String key : keys)
		{
			translationKey = translationPrefix + key;
			translation = Translatrix.getTranslationString(translationKey);
			if (translation == null || translation.equals(translationKey))
				translation = key;
			
			resultList.add(new ComboBoxElement<String>(translation, key));
		}
		
		if (orderedByTranslation)
			Collections.sort(resultList);
		
		return resultList;
	} 
	
	
	public static <T extends AutoTranslatable> int getIndexByDefaultTranslation (JComboBox comboBox, String translationKey)
	{
		return getIndex(comboBox, translationKey, false);
	}
	
	
	public static <T extends AutoTranslatable> int getIndexByKey (JComboBox comboBox, String translationKey)
	{
		return getIndex(comboBox, translationKey, true);
	}
	
	
	private static <T extends AutoTranslatable> int getIndex (JComboBox comboBox, String key, boolean byTranslationKey)
	{
		ComboBoxElement<T>	currentItem;
		
		if (key == null)
			return -1;
		
		String tmpKey;
		for (int index = 0; index < comboBox.getItemCount(); index++)
		{
			currentItem = (ComboBoxElement<T>) comboBox.getItemAt(index);
			if (currentItem == null || currentItem.getValue() == null)
				continue;
			
			if (byTranslationKey)
				tmpKey = currentItem.getValue().getTranslationKey();
			else
				tmpKey = currentItem.getValue().getDefaultTranslation();
			
			if (key.equals(tmpKey))
				return index;
		}
		
		return -1;
	}


	public static ImageIcon createGrayImage (ImageIcon coloredImage)
	{
		return new ImageIcon(GrayFilter.createDisabledImage(coloredImage.getImage()));
	}
	
	
	public static JDateChooser getDateChooser(boolean longVersion) {
		final String dateFormat = getDateFormatString(longVersion);
		final String dateMask 	= getDateMaskString();
		final SimpleDateFormat df = new SimpleDateFormat(dateFormat);
		
//		JSpinnerDateEditor l_DateEditor = new JSpinnerDateEditor ();
//		l_DateEditor.setDateFormatString(dateFormat);   
//		JDateChooser jd = new JDateChooser(null,dateFormat,l_DateEditor);
		
		final JDateChooser jd = new JDateChooser(dateFormat, dateMask, '_');
		jd.getDateEditor().getUiComponent().addFocusListener(new FocusAdapter() {
		    @Override
		    public void focusGained(FocusEvent evt) {
		        ((JTextFieldDateEditor)evt.getSource()).selectAll();
		    }
		    @Override
		    public void focusLost(FocusEvent evt) {
		    	try {
		    		String dateText = ((JTextFieldDateEditor)evt.getSource()).getText();
		    		Calendar c = new GregorianCalendar();
		    		c.setTime(df.parse(dateText));
		    		if (c.get(Calendar.YEAR) < 100) {
		    			c.add( Calendar.YEAR, 2000);
		    			jd.setDate(c.getTime());
		    		}					
				} catch (Exception e) {
					logger.info("unable to parse date");
				}
		    }
		});
		jd.getDateEditor().getUiComponent().addKeyListener(new KeyAdapter() {
			public void keyReleased(KeyEvent evt) {
				if (! (evt.getKeyCode() == KeyEvent.VK_ENTER) ) return;
				
				try {
		    		String dateText = ((JTextFieldDateEditor)evt.getSource()).getText();
		    		Calendar c = new GregorianCalendar();
		    		c.setTime(df.parse(dateText));
		    		if (c.get(Calendar.YEAR) < 100) {
		    			c.add( Calendar.YEAR, 2000);
		    			jd.setDate(c.getTime());
		    		}					
				} catch (Exception e) {
					logger.info("unable to parse date");
				}
			}
		});
		
		
		jd.setDateFormatString(dateFormat);
		
		return jd;
	}
	
	public static String getDateFormatString(boolean longVersion) {
		Locale l = Locale.getDefault();

		if (l != null) {
			if (Locale.FRANCE.equals(l)) {
				return DATEFORMAT_FRENCH_SHORT;
			} else if (Locale.GERMANY.equals(l)) {
				return DATEFORMAT_GERMAN_SHORT;
			} else {
				return DATEFORMAT_ENGLISH_SHORT;
			}
		}
		return DATEFORMAT_ENGLISH_SHORT;
	}
	
	public static String getDateMaskString() {
		Locale l = Locale.getDefault();

		if (l != null) {
			if (Locale.FRANCE.equals(l)) {
				return DATEFORMAT_FRENCH_SHORT.replaceAll("\\w", "#");
			} else if (Locale.GERMANY.equals(l)) {
				return DATEFORMAT_GERMAN_SHORT.replaceAll("\\w", "#");
			} else {
				return DATEFORMAT_ENGLISH_SHORT.replaceAll("\\w", "#");
			}
		}
		return DATEFORMAT_ENGLISH_SHORT.replaceAll("\\w", "#");
	}
	
	public static DateFormat getDateFormat(boolean longVersion) {
		Locale l = Locale.getDefault();

		if (l != null) {
			if (Locale.FRANCE.equals(l)) {
				return new SimpleDateFormat(DATEFORMAT_FRENCH_SHORT);
			} else if (Locale.US.equals(l)) {
				return new SimpleDateFormat(DATEFORMAT_ENGLISH_SHORT);
			} else if (Locale.GERMANY.equals(l)) {
				return new SimpleDateFormat(DATEFORMAT_GERMAN_SHORT);
			} else {
				return DateFormat.getDateInstance(DateFormat.SHORT, l);
			}
		}
		return DateFormat.getDateInstance(DateFormat.SHORT);
	}
	
	public static String getTimeFormatString(boolean longVersion) {
		if (longVersion) {
			return TIMEFORMAT_LONG;
		} else {
			return TIMEFORMAT;
		}
	}

	public static DateFormat getTimeFormat(boolean longVersion) {
		Locale l = Locale.getDefault();

		if (longVersion) {
			if (l != null) {
				return new SimpleDateFormat (TIMEFORMAT_LONG,l);
			}
			return new SimpleDateFormat (TIMEFORMAT_LONG);
		} else {
			if (l != null) {
				return new SimpleDateFormat (TIMEFORMAT,l);
			}
			return new SimpleDateFormat (TIMEFORMAT);	
		}
	}
	
	public static DateFormat getDateTimeFormat(boolean longVersion) {
		return new SimpleDateFormat(getDateFormatString(longVersion) + " " + getTimeFormatString(longVersion));
	}
}
