/*
 * This file is part of Nuts Framework.
 * Copyright(C) 2009-2012 Nuts Develop Team.
 *
 * Nuts Framework is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License any later version.
 * 
 * Nuts Framework 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Nuts Framework. If not, see <http://www.gnu.org/licenses/>.
 */
package nuts.core.util.converter;

import nuts.core.collections.MessageFormatKey;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;


public class DateConverter extends AbstractConverter<Date> {
	/**
	 * DateFormat cache
	 */
	private Map<MessageFormatKey, DateFormat> formatCache = new ConcurrentHashMap<MessageFormatKey, DateFormat>();

	public static String DATE_FORMAT = "yyyy/MM/dd HH:mm:ss.SSS";
	public static String DATE_FORMAT_TIMESTAMP = DATE_FORMAT;
	public static String DATE_FORMAT_DATETIME = "yyyy/MM/dd HH:mm:ss";
	public static String DATE_FORMAT_DATE = "yyyy/MM/dd";
	public static String DATE_FORMAT_TIME = "HH:mm:ss";

	public static String[] DATE_FORMATS = {
		DATE_FORMAT_TIMESTAMP,
		DATE_FORMAT_DATETIME,
		DATE_FORMAT_DATE,
		DATE_FORMAT_TIME,
	};

	public DateConverter() {
		super(Date.class);
	}
	
	protected Date convertValue(Object value) {
		if (value instanceof Number) {
			Number num = (Number)value;
			return new Date(num.longValue());
		}
		
		ParseException ex = null;
		for (String f : DATE_FORMATS) {
			try {
				DateFormat df = getDateFormat(f, Locale.getDefault(), null);
				if (df != null) {
					return df.parse(f);
				}
			}
			catch (ParseException e) {
				ex = e;
			}
		}
		throw new ConvertException("Failed to convert date: " + value.toString(), ex);
	}

	/**
	 * get the cached DateFormat
	 * @param pattern pattern string
	 * @param locale locale
	 * @param timezone time zone
	 * @return cached DateFormat
	 */
	private DateFormat getCachedDateFormat(String pattern, Locale locale, TimeZone timezone) {
		MessageFormatKey key = new MessageFormatKey(pattern, locale, timezone);
		return formatCache.get(key);
	}
	
	/**
	 * set the DateFormat to cache
	 * @param dateForamt DateFormat object
	 * @param pattern pattern string
	 * @param locale locale
	 * @param timezone time zone
	 */
	private void setCachedDateFormat(DateFormat dateForamt, String pattern, Locale locale, TimeZone timezone) {
		MessageFormatKey key = new MessageFormatKey(pattern, locale, timezone);
		formatCache.put(key, dateForamt);
	}
	

	/**
	 * getDateFormat
	 *
	 * @param pattern pattern
	 * @param locale locale
	 * @param timezone time zone
	 * @return DateFormat object
	 */
	public DateFormat getDateFormat(String pattern, Locale locale, TimeZone timezone) {
		DateFormat df = getCachedDateFormat(pattern, locale, timezone);
		if (df == null) {
			try {
				df = new SimpleDateFormat(pattern, locale);
				if (timezone != null) {
					df.setTimeZone(timezone);
				}
			}
			catch (Exception e) {
				throw new IllegalArgumentException("The DateFormat pattern [" + pattern + "] is invalid.", e);
			}
			setCachedDateFormat(df, pattern, locale, timezone);
		}
		return df;
	}
}
