package eu.dnetlib.miscutils.datetime;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import org.dom4j.DocumentException;

/**
 * various date utilities.
 * 
 * @author michele
 * 
 */
public class DateUtils {

	private static final SimpleDateFormat ISO8601FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US);

	private static final SimpleDateFormat OUTFORMAT = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'", Locale.US);

	private final static List<String> INPUTFORMATS = Arrays.asList("yyyy-MM-dd'T'hh:mm:ss", "yyyy-MM-dd", "dd-MM-yyyy", "dd/MM/yyyy", "yyyy");

	private transient final Date date;

	public DateUtils() {
		this.date = new Date();
	}

	public DateUtils(final Date date) {
		this.date = (Date) date.clone();
	}

	public String getDateAsISO8601String() {
		String result;
		synchronized (ISO8601FORMAT) {
			result = ISO8601FORMAT.format(this.date);
		}

		//convert YYYYMMDDTHH:mm:ss+HH00 into YYYYMMDDTHH:mm:ss+HH:00
		//- note the added colon for the Timezone
		return result.substring(0, result.length() - 2) + ":" + result.substring(result.length() - 2);
	}

	public Long getPrecisionTime() {
		return this.date.getTime();
	}

	public static long now() {
		return new Date().getTime();
	}

	public static String now_ISO8601() { // NOPMD
		String result;
		synchronized (ISO8601FORMAT) {
			result = ISO8601FORMAT.format(new Date());
		}
		//convert YYYYMMDDTHH:mm:ss+HH00 into YYYYMMDDTHH:mm:ss+HH:00
		//- note the added colon for the Timezone
		return result.substring(0, result.length() - 2) + ":" + result.substring(result.length() - 2);
	}

	/**
	 * parses a iso8601 date into a date.
	 * 
	 * @param dateArg
	 *            string iso8601 date
	 * @return date object
	 */
	public Date parse(final String dateArg) {
		String date = dateArg;

		if ("".equals(date))
			return null;

		try {
			if (date.endsWith("Z")) {
				date = date.replace("Z", "+0000");
			} else if (date.length() < 20) {
				date += "+0000"; // NOPMD
			} else {
				final String end = date.substring(date.length() - 3);
				date = date.substring(0, date.length() - 3) + end.replace(":", "");
			}

			synchronized (ISO8601FORMAT) {
				return ISO8601FORMAT.parse(date);
			}
		} catch (ParseException e) {
			throw new IllegalStateException("invalid iso8601 date '" + dateArg + "' (even after normalizing it to '" + date + "')");
		}
	}

	public static String calculate_ISO8601(long l) {
		String result = ISO8601FORMAT.format(new Date(l));
		// convert YYYYMMDDTHH:mm:ss+HH00 into YYYYMMDDTHH:mm:ss+HH:00
		//- note the added colon for the Timezone
		result = result.substring(0, result.length() - 2) + ":" + result.substring(result.length() - 2);
		return result;
	}

	/**
	 * Extracts the duration part of a ISO8601 formatted date. Duration part is the part after the 'T'.
	 * 
	 * @param dateArg
	 * @return
	 */
	public String getDuration(final String dateArg) {
		String[] splitted = dateArg.split("T");
		if (splitted.length == 1)
			return "0";
		else
			return splitted[1];
	}

	/**
	 * method return a solr-compatible string representation of a date
	 *
	 * @param date
	 * @return
	 * @throws DocumentException
	 * @throws ParseException
	 */
	public static String getParsedDateField(final String date) {
		for (String formatString : INPUTFORMATS) {
			try {
				return OUTFORMAT.format(new SimpleDateFormat(formatString).parse(date));
			} catch (ParseException e) {}
		}
		throw new IllegalStateException("unable to parse date: " + date);
	}
}
