package org.pmdreports.util;

import java.security.InvalidParameterException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

/**
 * A collection of helpful methods using java dates and calendars.
 * 
 * @author Ben Northrop Date: August 14th, 2002
 */
public final class DateUtil {

	// CONSTRUCTORS
	/**
	 * Private constructor since utilities class.
	 */
	private DateUtil() {
		// do nothing
	}

	// CONSTANTS
	/**
	 * Days of week.
	 */
	public static final int MONDAY = Calendar.MONDAY;
	public static final int TUESDAY = Calendar.TUESDAY;
	public static final int WEDNESDAY = Calendar.WEDNESDAY;
	public static final int THURSDAY = Calendar.THURSDAY;
	public static final int FRIDAY = Calendar.FRIDAY;
	public static final int SATURDAY = Calendar.SATURDAY;
	public static final int SUNDAY = Calendar.SUNDAY;

	// METHODS
	/**
	 * Test whether two dates are the same day, meaning that their day, month,
	 * and year are all equal. Using the equals() method from the
	 * java.util.Date() will compare the dates to the millisecond. If either of
	 * the dates are null, then they are considered not equal.
	 * 
	 * @author Ben Northrop Date: August 14th, 2002
	 */
	public static boolean datesAreEqual(Date d1, Date d2) {
		if (d1 == null || d2 == null) {
			return false;
		}

		Calendar c1 = new GregorianCalendar();
		Calendar c2 = new GregorianCalendar();
		c1.setTime(d1);
		c2.setTime(d2);

		if (c1.get(Calendar.DATE) == c2.get(Calendar.DATE)
				&& c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH)
				&& c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR)) {
			return true;
		}

		return false;
	}

	/**
	 * Convert a java.util.Date to a java.sql.Date.
	 * 
	 * @author Ben Northrop Date: August 14th, 2002
	 */
	public static java.sql.Date convertToSqlDate(java.util.Date date) {
		if (date == null) {
			return null;
		}

		long time = date.getTime();
		return new java.sql.Date(time);
	}

	/**
	 * Determine whether the current system time is in the afternoon.
	 * 
	 * @author Ben Northrop Date: August 17th, 2002
	 */
	public static boolean isAfternoon() {
		return isAfternoon(new Date());
	}

	/**
	 * Determine whether a given java.util.Date is in the afternoon.
	 * 
	 * @author Ben Northrop Date: August 17th, 2002
	 */
	public static boolean isAfternoon(Date date) {
		if (date == null) {
			return false;
		}

		Calendar calendar = new GregorianCalendar();
		calendar.setTime(date);
		int hour = calendar.get(Calendar.HOUR_OF_DAY);
		if (hour < 12) {
			return false;
		}

		return true;
	}

	/**
	 * Add a specified number of days to the date.
	 * 
	 * @author Ben Northrop Date: August 19th, 2002
	 */
	public static Date addDays(Date date, int numDays) {
		if (date == null) {
			return null;
		}

		Calendar calendar = new GregorianCalendar();
		calendar.setTime(date);
		calendar.add(Calendar.DATE, numDays);
		return calendar.getTime();
	}

	/**
	 * Determine whether the day of the calendar is less than the day of
	 * another.
	 * 
	 * @author Ben Northrop Date: August 30th, 2002
	 * 
	 * @param int
	 *            The unit that is used to determine, valid values being:
	 *            Calendar.DAY, Calendar.MONTH, Calendar.YEAR. Any other will
	 *            just use the calendar.before() method.
	 * @param Calendar
	 *            The calendar that is being compared.
	 * @param Calendar
	 *            The calendar that is being compared against.
	 */
	public static boolean isCalendarBefore(int unit, Calendar c1, Calendar c2) {
		// Validate if null
		if (c1 == null || c2 == null) {
			throw new InvalidParameterException(
					"DateUtilities.isCalendarDayBefore(): parameter is null");
		}

		if (unit == Calendar.YEAR) {
			return isYearBefore(c1, c2);
		} else if (unit == Calendar.MONTH) {
			return isMonthBefore(c1, c2);
		} else if (unit == Calendar.DATE) {
			return isDateBefore(c1, c2);
		} else {
			return c1.before(c2);
		}
	}

	/**
	 * Return true if c1's date is before c2's date.
	 */
	private static boolean isDateBefore(Calendar c1, Calendar c2) {
		return c1.get(Calendar.DATE) < c2.get(Calendar.DATE);
	}

	/**
	 * Return true if c1's month is before c2's month.
	 */
	private static boolean isMonthBefore(Calendar c1, Calendar c2) {
		return c1.get(Calendar.MONTH) < c2.get(Calendar.MONTH);
	}

	/**
	 * Return true if c1 is before c2.
	 */
	private static boolean isYearBefore(Calendar c1, Calendar c2) {
		return c1.get(Calendar.YEAR) < c2.get(Calendar.YEAR);
	}

	/**
	 * Return true if d1's date is before d2's date.
	 */
	public static boolean isDateBefore(Date d1, Date d2) {
		Calendar c1 = new GregorianCalendar();
		c1.setTime(d1);
		Calendar c2 = new GregorianCalendar();
		c2.setTime(d2);
		return isDateBefore(c1, c2);
	}

	/**
	 * Format a calendar in dd/mm/yyyy hh:mm:ss.
	 * 
	 * @author Ben Northrop Date August 30th, 2002
	 */
	@SuppressWarnings("PMD.AddEmptyString")
	public static String formatCalendar(Calendar c1) {
		if (c1 == null) {
			return "null";
		}

		int year = c1.get(Calendar.YEAR);
		int month = c1.get(Calendar.MONTH) + 1;
		int date = c1.get(Calendar.DATE);
		int hour = c1.get(Calendar.HOUR);
		int minute = c1.get(Calendar.MINUTE);
		int second = c1.get(Calendar.SECOND);

		return "" + month + '/' + date + '/' + year + ' ' + hour + ':' + minute
				+ ':' + second;
	}

	/**
	 * convert a java.sql.Date to String based on the mask passed in
	 * 
	 * @author Zemin Xu Date July 8th, 2003
	 */
	public static String formatDate(Date date, String mask) {
		SimpleDateFormat formatter = new SimpleDateFormat(mask); // NOPMD

		String dateStr = formatter.format(date);

		return dateStr;

	}

	/**
	 * Determine whether a given Date falls on a specific day of the week. Use
	 * the static finals in this class (which are the same as the Calendar)
	 * class to indicate the day of the week.
	 * 
	 * @author Ben Northrop Date: March 10th, 2003
	 */
	public static boolean isDateOnDay(Date date, int dayOfWeek) {
		if (date == null) {
			return false;
		}

		Calendar calendar = new GregorianCalendar();
		calendar.setTime(date);
		if (dayOfWeek == calendar.get(Calendar.DAY_OF_WEEK)) {
			return true;
		}

		return false;
	}

	/**
	 * Get the number of days in between two Dates. This may not be the fastest
	 * implementation from a performance stand point - if performance issues are
	 * encountered, please refactor.
	 * 
	 * @author Ben Northrop, April 9th, 2003
	 * @param Date
	 *            start date
	 * @param Date
	 *            end date
	 */
	public static int calculateDaysBetween(final Date startDate,
			final Date endDate) {
		if (startDate == null || endDate == null) {
			throw new InvalidParameterException(
					"start date or end date parameters are null");
		}

		if (datesAreEqual(startDate, endDate)) {
			return 0;
		}

		boolean swapDates = false;
		Date tmpStartDate = startDate;
		Date tmpEndDate = endDate;
		if (tmpEndDate.before(tmpStartDate)) {
			Date tmpDate = tmpStartDate;
			tmpStartDate = tmpEndDate;
			tmpEndDate = tmpDate;
			swapDates = true;
		}

		int numDays = 0;
		while (!datesAreEqual(tmpStartDate, tmpEndDate)) {
			tmpStartDate = addDays(tmpStartDate, 1);
			numDays++;
		}

		return swapDates ? numDays * -1 : numDays;
	}

	/**
	 * Get the day of the month for a given date.
	 * 
	 * @author Ben Northrop
	 * @param Date
	 *            date to determine the day of the month for
	 * @return int the day of the month
	 */
	public static int getDayOfMonth(Date date) {
		if (date == null) {
			throw new InvalidParameterException("Incoming date is null!");
		}

		Calendar calendar = new GregorianCalendar();
		calendar.setTime(date);
		return calendar.get(Calendar.DAY_OF_MONTH);
	}

	/**
	 * Get the month for todays date.
	 */
	public static int getMonthForToday() {
		Date today = new Date();
		Calendar calendar = new GregorianCalendar();
		calendar.setTime(today);
		return calendar.get(Calendar.MONTH) + 1;
	}

	/**
	 * Get the month for todays date.
	 */
	public static int getYearForToday() {
		Date today = new Date();
		Calendar calendar = new GregorianCalendar();
		calendar.setTime(today);
		return calendar.get(Calendar.YEAR);
	}
}
