package horstmann.ch03_day3;
public class Day
{
	/**
      Constructs a day with a given year, month, and day
      of the Julian/Gregorian calendar. The Julian calendar
      is used for all days before October 15, 1582
      @param aYear a year != 0
      @param aMonth a month between 1 and 12
      @param aDate a date between 1 and 31
	 */
	public Day(int aYear, int aMonth, int aDate)
	{
		year = aYear;
		month = aMonth;
		date = aDate;
		ymdValid = true;
		julianValid = false;
	}

	/**
      Returns the year of this day
      @return the year
	 */
	public int getYear()
	{
		ensureYmd();
		return year;
	}

	/**
      Returns the month of this day
      @return the month
	 */
	public int getMonth()
	{
		ensureYmd();
		return month;
	}

	/**
      Returns the day of the month of this day
      @return the day of the month
	 */
	public int getDate()
	{
		ensureYmd();
		return date;
	}

	/**
      Returns a day that is a certain number of days away from
      this day
      @param n the number of days, can be negative
      @return a day that is n days away from this one
	 */
	public Day addDays(int n)
	{
		ensureJulian();
		return new Day(julian + n);
	}

	/**
      Returns the number of days between this day and another
      day
      @param other the other day
      @return the number of days that this day is away from
      the other (>0 if this day comes later)
	 */
	public int daysFrom(Day other)
	{
		ensureJulian();
		other.ensureJulian();
		return julian - other.julian;
	}

	private Day(int aJulian)
	{
		julian = aJulian;
		ymdValid = false;
		julianValid = true;
	}

	/**
      Computes the Julian day number of this day if
      necessary
	 */
	private void ensureJulian()
	{
		if (julianValid) return;
		julian = toJulian(year, month, date);
		julianValid = true;
	}

	/**
      Converts this Julian day mumber to a calendar date if necessary.
	 */
	private void ensureYmd()
	{
		if (ymdValid) return;
		int[] ymd = fromJulian(julian);
		year = ymd[0];
		month = ymd[1];
		date = ymd[2];
		ymdValid = true;
	}

	/**
      Computes the Julian day number of the given day day.

      @param year a year
      @param month a month
      @param date a day of the month
      @return The Julian day number that begins at noon of
      the given day
      Positive year signifies CE, negative year BCE.
      Remember that the year after 1 BCE is 1 CE.

      A convenient reference point is that May 23, 1968 noon
      is Julian day number 2440000.

      Julian day number 0 is a Monday.

      This algorithm is from Press et al., Numerical Recipes
      in C, 2nd ed., Cambridge University Press 1992
	 */
	private static int toJulian(int year, int month, int date)
	{
		int jy = year;
		if (year < 0) jy++;
		int jm = month;
		if (month > 2) jm++;
		else
		{
			jy--;
			jm += 13;
		}
		int jul = (int) (java.lang.Math.floor(365.25 * jy)
				+ java.lang.Math.floor(30.6001 * jm) + date + 1720995.0);

		int IGREG = 15 + 31 * (10 + 12 * 1582);
		// Gregorian Calendar adopted Oct. 15, 1582

		if (date + 31 * (month + 12 * year) >= IGREG)
			// Change over to Gregorian calendar
		{
			int ja = (int) (0.01 * jy);
			jul += 2 - ja + (int) (0.25 * ja);
		}
		return jul;
	}

	/**
      Converts a Julian day number to a calendar date.

      This algorithm is from Press et al., Numerical Recipes
      in C, 2nd ed., Cambridge University Press 1992

      @param j  the Julian day number
      @return an array whose 0 entry is the year, 1 the month,
      and 2 the day of the month.
	 */
	@SuppressWarnings("cast")
	private static int[] fromJulian(int j)
	{
		int ja = j;

		int JGREG = 2299161;
		// The Julian day number of the adoption of the Gregorian calendar

		if (j >= JGREG)
			// Cross-over to Gregorian Calendar produces this correction
		{
			int jalpha = (int) (((float) (j - 1867216) - 0.25)
					/ 36524.25);
			ja += 1 + jalpha - (int) (0.25 * jalpha);
		}
		int jb = ja + 1524;
		int jc = (int) (6680.0 + ((float) (jb - 2439870) - 122.1)
				/ 365.25);
		int jd = (int) (365 * jc + (0.25 * jc));
		int je = (int) ((jb - jd) / 30.6001);
		int date = jb - jd - (int) (30.6001 * je);
		int month = je - 1;
		if (month > 12) month -= 12;
		int year = jc - 4715;
		if (month > 2) --year;
		if (year <= 0) --year;
		return new int[] { year, month, date };
	}

	private int year;
	private int month;
	private int date;
	private int julian;
	private boolean ymdValid;
	private boolean julianValid;
}





