/*
 * 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.exts.xwork2.converter.converters;

import nuts.core.castor.Castors;
import nuts.core.castor.castors.DateTypeCastor.DateCastor;
import nuts.core.lang.Arrays;
import nuts.core.lang.Numbers;
import nuts.core.lang.Strings;

import java.text.DateFormat;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;

/**
 * DateTypeConverter
 */
public class DateTypeConverter extends AbstractTypeConverter {
	/**
	 * DATE_TIMEZONE = "date-timezone";
	 */
	public static final String DATE_TIMEZONE = "date-timezone";

	/**
	 * 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 (Arrays.isEmpty(values) || Strings.isEmpty(values[0])) {
			return null;
		}

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

		if (d == null) {
			for (String f : formats) {
				try {
					DateFormat df = getDateFormat(f);
					if (df != null) {
						d = df.parse(values[0]);
						break;
					}
				}
				catch (ParseException e) {
				}
			}
		}
		
		if (d == null) {
			Long t = Numbers.toLong(values[0]);
			if (t != null) {
				d = new Date(t);
			}
		}

		if (d == null) {
			throw new IllegalArgumentException("Can't convert [" + values[0] + "] to [" + toClass + "].");
		}
		
		if (Calendar.class.equals(toClass)) {
			Calendar c = Calendar.getInstance();
			c.setTime(d);
			return c;
		}

		return d;
	}

	/**
	 * 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();
	}

	private TimeZone getTimeZone() {
		String tz = getText(DATE_TIMEZONE);
		if (tz != null) {
			return TimeZone.getTimeZone(tz);
		}
		return null;
	}

	/**
	 * 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 (Strings.isEmpty(pattern)) {
			return null;
		}
		return getDateFormat(pattern, getLocale(), getTimeZone());
	}

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

	/**
	 * getDateFormat
	 *
	 * @param pattern pattern
	 * @param locale locale
	 * @param timezone time zone
	 * @return DateFormat object
	 */
	public static DateFormat getDateFormat(String pattern, Locale locale, TimeZone timezone) {
		DateCastor dc = (DateCastor)Castors.me().getCastor(Date.class);
		return dc.getDateFormat(pattern, locale, timezone);
	}
}
