/*
 * Copyright (c) 2006-2009 OrangeSignal.com All rights reserved.
 * 
 * これは Apache ライセンス Version 2.0 (以下、このライセンスと記述) に
 * 従っています。このライセンスに準拠する場合以外、このファイルを使用
 * してはなりません。このライセンスのコピーは以下から入手できます。
 * 
 * http://www.apache.org/licenses/LICENSE-2.0.txt
 * 
 * 適用可能な法律がある、あるいは文書によって明記されている場合を除き、
 * このライセンスの下で配布されているソフトウェアは、明示的であるか暗黙の
 * うちであるかを問わず、「保証やあらゆる種類の条件を含んでおらず」、
 * 「あるがまま」の状態で提供されるものとします。
 * このライセンスが適用される特定の許諾と制限については、このライセンス
 * を参照してください。
 */

package jp.sf.orangesignal.chart.util;

import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

import jp.sf.orangesignal.ta.util.Assert;

/**
 * 日本の祝日に関するユーティリティクラスです。
 * 
 * @author 杉澤 浩二
 * @deprecated 未使用
 */
public abstract class JaHolidayUtils {

	/**
	 * 昭和天皇の大喪の礼(1989/2/24)
	 */
	private static final Calendar FUNERAL_CEREMONY_OF_EMPEROR_SHOWA;

	/**
	 * 即位礼正殿の儀(1990/11/12)
	 */
	private static final Calendar CEREMONY_OF_THE_ENTHRONEMENT;

	/**
	 * 皇太子徳仁親王の結婚の儀(1993/6/9)
	 */
	private static final Calendar RITE_OF_WEDDING_OF_HIH_CROWN_PRINCE_NARUHITO;

	static {
		FUNERAL_CEREMONY_OF_EMPEROR_SHOWA = getCalendar();
		FUNERAL_CEREMONY_OF_EMPEROR_SHOWA.set(1989,  Calendar.FEBRUARY, 24);
		truncateTime(FUNERAL_CEREMONY_OF_EMPEROR_SHOWA);

		CEREMONY_OF_THE_ENTHRONEMENT = getCalendar();
		CEREMONY_OF_THE_ENTHRONEMENT.set(1990, Calendar.NOVEMBER, 12);
		truncateTime(CEREMONY_OF_THE_ENTHRONEMENT);

		RITE_OF_WEDDING_OF_HIH_CROWN_PRINCE_NARUHITO = getCalendar();
		RITE_OF_WEDDING_OF_HIH_CROWN_PRINCE_NARUHITO.set(1993,  Calendar.JUNE,  9);
		truncateTime(RITE_OF_WEDDING_OF_HIH_CROWN_PRINCE_NARUHITO);
	}

	private static Calendar getCalendar() { return Calendar.getInstance(TimeZone.getTimeZone("Asia/Tokyo")); }

	private static void truncateTime(final Calendar c) {
		c.set(Calendar.HOUR_OF_DAY, 0);
		c.set(Calendar.MINUTE, 0);
		c.set(Calendar.SECOND, 0);
		c.set(Calendar.MILLISECOND, 0);
	}

	/**
	 * 国民の祝日であるかどうかを返します。
	 * 
	 * @param date 日付
	 * @return 国民の祝日であるかどうか
	 */
	public static boolean isHoliday(final Date date) {
		final Calendar c = getCalendar();
		c.setTime(date);
		truncateTime(c);

		final int year = c.get(Calendar.YEAR);
		final int month = c.get(Calendar.MONTH);
		final int day = c.get(Calendar.DAY_OF_MONTH);		// 日を取得
		final int dayofweek = c.get(Calendar.DAY_OF_WEEK);	// 曜日を取得

		// 休日判定
		switch (month) {
			case Calendar.JANUARY:
				// 元旦(1949～) [New Year's Day]
				if (day == 1)
					return true;

				// 成人の日(1949～) [Coming of Age Day]
				if (year >= 2000) {
					// 2000 年以降は第２月曜日
					if (c.get(Calendar.DAY_OF_WEEK_IN_MONTH) == 2 && dayofweek == Calendar.MONDAY)
						return true;
				} else {
					if (day == 15)
						return true;
				}
				break;

			case Calendar.FEBRUARY:
				// 建国記念の日(1967～) [National Foundation Day]
				if (day == 11)
					return true;
				break;

			case Calendar.MARCH:
				// 春分の日(1949～) ※3月21日頃 [Vernal Equinox Day]
				if (day == getVernalEquinoxDay(year))
					return true;
				break;

			case Calendar.APRIL:
				// 昭和天皇誕生日(1949～) → (旧)みどりの日 [Greenery Day] → 昭和の日 [Showa Day]
				if (day == 29)
					return true;
				break;

			case Calendar.MAY:
				// 憲法記念日(1949～) [Constitution Memorial Day]
				if (day == 3)
					return true;
				// 国民の休日(1986～)、みどりの日
				if (year >= 1986 && day == 4)
					return true;
				// こどもの日(1949～) [Children's Day]
				if (day == 5)
					return true;
				break;

			case Calendar.JULY:
				// 海の日(1996～) [Marine Day]
				if (year >= 1996) {
					if (year >= 2003) {
						// 2003 年以降は第三月曜日
						if (c.get(Calendar.DAY_OF_WEEK_IN_MONTH) == 3 && dayofweek == Calendar.MONDAY)
							return true;
					} else {
						if (day == 20)
							return true;
					}
				}
				break;

			case Calendar.SEPTEMBER:
				// 敬老の日(1966年～) [Respect for the Aged Day]
				if (year >= 2003) {
					// 2003 年以降は第三月曜日
					if (c.get(Calendar.DAY_OF_WEEK_IN_MONTH) == 3 && dayofweek == Calendar.MONDAY)
						return true;
				} else {
					if (day == 15)
						return true;
				}

				// 秋分の日(1948年～) [Autumnal Equinox Day]
				if (day == getAutumnalEquinoxDay(year))
					return true;
				break;

			case Calendar.OCTOBER:
				// 体育の日(1966年～) [Health and Sports Day]
				if (year >= 2000) {
					// 2000 年以降は第２月曜日
					if (c.get(Calendar.DAY_OF_WEEK_IN_MONTH) == 2 && dayofweek == Calendar.MONDAY)
						return true;
				} else {
					if (day == 10)
						return true;
				}
				break;

			case Calendar.NOVEMBER:
				// 文化の日(1948年～) [National Culture Day]
				if (day == 3)
					return true;
				// 勤労感謝の日(1948年～) [Labor Thanksgiving Day]
				if (day == 23)
					return true;
				break;

			case Calendar.DECEMBER:
				// 天皇誕生日(1989～) [Emperor's Birthday]
				if (year >= 1989 && day == 23)
					return true;
				break;
		}

		if (c.compareTo(FUNERAL_CEREMONY_OF_EMPEROR_SHOWA) == 0)
			return true;
		if (c.compareTo(CEREMONY_OF_THE_ENTHRONEMENT) == 0)
			return true;
		if (c.compareTo(RITE_OF_WEDDING_OF_HIH_CROWN_PRINCE_NARUHITO) == 0)
			return true;

		return false;
	}

	private static final double FRACTION = 0.242194;//0.2421904;

	/**
	 * 指定された年の春分の日を求めます。
	 * 
	 * @param year 年(1851年～2150年)
	 * @return 指定された年の春分の日
	 */
	public static strictfp int getVernalEquinoxDay(final int year) {
		Assert.isFalse(year > 2150);

		if (year >= 2100)
			return (int) (21.8510 + FRACTION * (year - 1980) - (year - 1980) / 4);
		if (year >= 1980)
			return (int) (20.8431 + FRACTION * (year - 1980) - (year - 1980) / 4);
		if (year >= 1900)
			return (int) (20.8357 + FRACTION * (year - 1980) - (year - 1983) / 4);
		if (year >= 1851)
			return (int) (19.8277 + FRACTION * (year - 1980) - (year - 1983) / 4);

		throw new IllegalArgumentException();
	}

	/**
	 * 指定された年の秋分の日を求めます。
	 * 
	 * @param year 年(1851年～2150年)
	 * @return 指定された年の秋分の日
	 */
	public static strictfp int getAutumnalEquinoxDay(final int year) {
		Assert.isFalse(year > 2150);

		if (year >= 2100)
			return (int) (24.2488 + FRACTION * (year - 1980) - (year - 1980) / 4);
		if (year >= 1980)
			return (int) (23.2488 + FRACTION * (year - 1980) - (year - 1980) / 4);
		if (year >= 1900)
			return (int) (23.2588 + FRACTION * (year - 1980) - (year - 1983) / 4);
		if (year >= 1851)
			return (int) (22.2588 + FRACTION * (year - 1980) - (year - 1983) / 4);

		throw new IllegalArgumentException();
	}

	/**
	 * 年末年始の休業日かどうかを返します。
	 * 
	 * @param date 日付
	 * @return 年末年始の休業日かどうか
	 */
	public static boolean isClosed(final Date date) {
		final Calendar c = getCalendar();
		c.setTime(date);

		final int month = c.get(Calendar.MONTH);
		final int day = c.get(Calendar.DAY_OF_MONTH);		// 日を取得

		switch (month) {
			case Calendar.JANUARY:	// 1月
				if (day == 1 || day == 2 || day == 3)
					return true;
				break;

			case Calendar.DECEMBER:	// 12月
				if (day == 31)
					return true;
				break;
		}

		return false;
	}

}
