/*
 * This file is part of Nuts Framework.
 * Copyright (C) 2009 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.exts.xwork2.converter.converters;

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

import nuts.core.collections.MessageFormatKey;
import nuts.core.lang.StringUtils;

import org.apache.commons.lang.ArrayUtils;

/**
 * DateTypeConverter
 */
public class DateTypeConverter extends AbstractTypeConverter {
	/**
	 * DateFormat cache
	 */
	private static Map<MessageFormatKey, DateFormat> formatCache = new ConcurrentHashMap<MessageFormatKey, DateFormat>();

	/**
	 * DATE_FORMAT_DEFAULT = "date-format";
	 */
	public static final String DATE_FORMAT_DEFAULT = "date-format";

	/**
	 * DATE_FORMAT_PREFIX = "date-format-";
	 */
	public static final String DATE_FORMAT_PREFIX = "date-format-";

	private static String[] formats = { "timestamp", "datetime", "date", "time", "cce" };

	/**
	 * @return the formats
	 */
	public static String[] getFormats() {
		return formats;
	}

	/**
	 * @param formats the formats to set
	 */
	public static void setFormats(String[] formats) {
		DateTypeConverter.formats = formats;
	}

	/**
	 * Converts one or more String values to the specified class.
	 * 
	 * @param context the action context
	 * @param values the String values to be converted, such as those submitted from an HTML form
	 * @param toClass the class to convert to
	 * @return the converted object
	 */
	public Object convertFromString(Map context, String[] values, Class toClass) {
		if (ArrayUtils.isEmpty(values) || StringUtils.isEmpty(values[0])) {
			return null;
		}

		try {
			DateFormat df = getDefaultDateFormat();
			if (df != null) {
				return df.parse(values[0]);
			}
		}
		catch (Exception e) {
		}

		for (String f : formats) {
			try {
				DateFormat df = getDateFormat(f);
				if (df != null) {
					return df.parse(values[0]);
				}
			}
			catch (Exception e) {
			}
		}

		try {
			long t = Long.parseLong(values[0]);
			Date d = new Date(t);
			if (Calendar.class.equals(toClass)) {
				Calendar c = Calendar.getInstance();
				c.setTime(d);
				return c;
			}
			else {
				return d;
			}
		}
		catch (Exception e) {
			throw new IllegalArgumentException("Can't convert [" + values[0] + "] to [" + toClass + "].");
		}
	}

	/**
	 * Converts the specified object to a String.
	 * 
	 * @param context the action context
	 * @param o the object to be converted
	 * @return the converted String
	 */
	public String convertToString(Map context, Object o) {
		if (o instanceof Calendar) {
			o = ((Calendar)o).getTime();
		}
		
		if (o instanceof Date) {
			DateFormat df = getDefaultDateFormat();
			if (df != null) {
				return df.format(o);
			}

			for (String f : formats) {
				df = getDateFormat(f);
				if (df != null) {
					return df.format(o);
				}
			}

			return String.valueOf(((Date)o).getTime());
		}
		return o == null ? null : o.toString();
	}

	/**
	 * get the cached DateFormat
	 * @param pattern pattern string
	 * @param locale locale
	 * @return cached DateFormat
	 */
	private static DateFormat getCachedDateFormat(String pattern, Locale locale) {
		MessageFormatKey key = new MessageFormatKey(pattern, locale);
		return formatCache.get(key);
	}
	
	/**
	 * set the DateFormat to cache
	 * @param pattern pattern string
	 * @param locale locale
	 * @param dateForamt DateFormat object
	 */
	private static void setCachedDateFormat(String pattern, Locale locale, DateFormat dateForamt) {
		MessageFormatKey key = new MessageFormatKey(pattern, locale);
		formatCache.put(key, dateForamt);
	}
	
	/**
	 * getDateFormat
	 *
	 * @param format format
	 * @return DateFormat object
	 */
	private DateFormat getDateFormat(String format) {
		String pattern = getText(format == null ? DATE_FORMAT_DEFAULT : DATE_FORMAT_PREFIX + format, (String)null);
		if (StringUtils.isEmpty(pattern)) {
			return null;
		}
		return getDateFormat(pattern, getLocale());
	}

	/**
	 * getDefaultDateFormat
	 *
	 * @param format format
	 * @return DateFormat object
	 */
	private DateFormat getDefaultDateFormat() {
		return getDateFormat(null);
	}

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