/*
 * MosP - Mind Open Source Project         http://www.mosp.jp/
 * Copyright (C) 1987-2009 MIND Co., Ltd.  http://www.e-mind.co.jp/
 * 
 * This program 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 2
 * of the License, or (at your option) any later version.
 * 
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package jp.mosp.common;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

/**
 * <p>祝祭日取得クラス</p>
 * （追加）<br>
 * ハッピーマンデー<br>
 * 春分の日、秋分の日の取得<br>
 * 祝日法による国民の祝日の取得
 * @author mind
 */
public class JapanHolidayUtility implements Serializable {

	private static final long serialVersionUID = -7341283435100183210L;

	private Calendar cal;
	private int yyyy;

	/**
	 * 成人の日 1月第2月曜日
	 */
	private static final int SEIJIN_MONTH = 1;
	/**
	 * 成人の日 1月第2月曜日
	 */	
	private static final int SEIJIN_DAY_OF_WEEK_IN_MONTH = 2;
	/**
	 * 成人の日
	 */	
	private static final String SEIJIN_NAME = "成人の日";

	/**
	 * 海の日 7月第3月曜日
	 */
	private static final int UMI_MONTH = 7;
	/**
	 * 海の日 7月第3月曜日
	 */
	private static final int UMI_DAY_OF_WEEK_IN_MONTH = 3;
	/**
	 * 海の日
	 */
	private static final String UMI_NAME = "海の日";

	/**
	 * 敬老の日 9月第3月曜日
	 */
	private static final int KEIROU_MONTH = 9;
	/**
	 * 敬老の日 9月第3月曜日
	 */
	private static final int KEIROU_DAY_OF_WEEK_IN_MONTH = 3;
	/**
	 * 敬老の日
	 */
	private static final String KEIROU_NAME = "敬老の日";

	/**
	 * 体育の日 10月第2月曜日
	 */
	private static final int TAIIKU_MONTH = 10;
	/**
	 * 体育の日 10月第2月曜日
	 */
	private static final int TAIIKU_DAY_OF_WEEK_IN_MONTH = 2;
	/**
	 * 体育の日
	 */
	private static final String TAIIKU_NAME = "体育の日";


	/**
	 * 春分の日 3月dd日
	 */
	private static final int SYUNBUN_MONTH = 3;
	/**
	 * 春分の日
	 */
	private static final String SYUNBUN_NAME = "春分の日";

	/**
	 * 秋分の日 9月dd日
	 */
	private static final int SYUUBUN_MONTH = 9;
	/**
	 * 秋分の日
	 */
	private static final String SYUUBUN_NAME = "秋分の日";


	/**
	 * 元日 1月1日
	 */
	private static final int GANJITSU_MONTH = 1;
	/**
	 * 元日 1月1日
	 */
	private static final int GANJITSU_DATE = 1;
	/**
	 * 元日
	 */
	private static final String GANJITSU_NAME = "元日";

	/**
	 * 建国記念の日 2月11日
	 */
	private static final int KENKOKUKINEN_MONTH = 2;
	/**
	 * 建国記念の日 2月11日
	 */
	private static final int KENKOKUKINEN_DATE = 11;
	/**
	 * 建国記念の日
	 */
	private static final String KENKOKUKINEN_NAME = "建国記念の日";

	/**
	 * 昭和の日 4月29日
	 */
	private static final int SYOUWA_MONTH = 4;
	/**
	 * 昭和の日 4月29日
	 */
	private static final int SYOUWA_DATE = 29;
	/**
	 * 昭和の日
	 */
	private static final String SYOUWA_NAME = "昭和の日";

	/**
	 * 憲法記念日 5月3日
	 */
	private static final int KENPOUKINEN_MONTH = 5;
	/**
	 * 憲法記念日 5月3日
	 */
	private static final int KENPOUKINEN_DATE = 3;
	/**
	 * 憲法記念日
	 */
	private static final String KENPOUKINEN_NAME = "憲法記念日";

	/**
	 * みどりの日 5月4日
	 */
	private static final int MIDORI_MONTH = 5;
	/**
	 * みどりの日 5月4日
	 */
	private static final int MIDORI_DATE = 4;
	/**
	 * みどりの日
	 */
	private static final String MIDORI_NAME = "みどりの日";

	/**
	 * こどもの日 5月5日
	 */
	private static final int KODOMO_MONTH = 5;
	/**
	 * こどもの日 5月5日
	 */
	private static final int KODOMO_DATE = 5;
	/**
	 * こどもの日
	 */
	private static final String KODOMO_NAME = "こどもの日";

	/**
	 * 文化の日 11月3日
	 */
	private static final int BUNKA_MONTH = 11;
	/**
	 * 文化の日 11月3日
	 */
	private static final int BUNKA_DATE = 3;
	/**
	 * 文化の日
	 */
	private static final String BUNKA_NAME = "文化の日";

	/**
	 * 勤労感謝の日 11月23日
	 */
	private static final int KINROUKANSYA_MONTH = 11;
	/**
	 * 勤労感謝の日 11月23日
	 */
	private static final int KINROUKANSYA_DATE = 23;
	/**
	 * 勤労感謝の日
	 */
	private static final String KINROUKANSYA_NAME = "勤労感謝の日";

	/**
	 * 天皇誕生日 12月23日
	 */
	private static final int TENNOUTANJOU_MONTH = 12;
	/**
	 * 天皇誕生日 12月23日
	 */
	private static final int TENNOUTANJOU_DATE = 23;
	/**
	 * 天皇誕生日
	 */
	private static final String TENNOUTANJOU_NAME = "天皇誕生日";

	/**
	 * 国民の休日
	 */
	private static final String KOKUMINNOKYUJITU_NAME = "国民の休日";


	/**
	 * 祝日配列
	 */
	private String[][] arySyuku = new String[15][2];

	/**
	 * 祝日名称
	 */
	private String[] arySyukuName = {
			SEIJIN_NAME,
			UMI_NAME,
			KEIROU_NAME,
			TAIIKU_NAME,
			SYUNBUN_NAME,
			SYUUBUN_NAME,
			GANJITSU_NAME,
			KENKOKUKINEN_NAME,
			KENPOUKINEN_NAME,
			KODOMO_NAME,
			BUNKA_NAME,
			KINROUKANSYA_NAME,
			TENNOUTANJOU_NAME
	};

	private String[][] aryCombined;

	/**
	 * コンストラクタ
	 */
	public JapanHolidayUtility() {
		this.cal = Calendar.getInstance();
		this.yyyy = 0;
	}

	/**
	 * コンストラクタ
	 * @param year 対象年度
	 * <p>祝祭日配列作成</p>
	 */
	public JapanHolidayUtility(int year) {
		this.cal = Calendar.getInstance();
		this.yyyy = year;
		createHoliday();
	}

	/**
	 * コンストラクタ
	 * @param y 年
	 * @param m 月
	 * @param d 日
	 */
	public JapanHolidayUtility(String y, String m, String d) {
		this.cal = Calendar.getInstance();
		cal.set(Integer.parseInt(y), Integer.parseInt(m) -1, Integer.parseInt(d));
	}

	/**
	 * コンストラクタ
	 * @param y 年
	 * @param m 月
	 */
	public JapanHolidayUtility(String y, String m) {
		this.cal = Calendar.getInstance();
		cal.set(Calendar.YEAR, Integer.parseInt(y));
		cal.set(Calendar.YEAR,  Integer.parseInt(m) -1);
	}


	/**
	 * 曜日取得メソッド
	 * @return 曜日
	 */
	public String retDayOfWeek() {
		int dw = cal.get(Calendar.DAY_OF_WEEK);
		List list = EnumYoubi.toArray();
		String youbi = (String)list.get(dw -1);
		return youbi;
	}

	/**
	 * 指定曜日可否メソッド
	 * @param youbi
	 * @return boolean
	 */
	public boolean isDayOfWeek(int youbi) {
		int dw = cal.get(Calendar.DAY_OF_WEEK);
		if (dw == youbi) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 指定曜日番目取得メソッド
	 * @return int
	 */
	public int retDayOfWeekinMonth() {
		return cal.get(Calendar.DAY_OF_WEEK_IN_MONTH);
	}

	/**
	 * 最大日数取得メソッド
	 * @return int
	 */
	public int retMaximumDay() {
		return cal.getActualMaximum(Calendar.DAY_OF_MONTH);
	}

	/**
	 * カレンダーsetter
	 * @param y 年
	 * @param m 月
	 */
	public void setYm(int y, int m) {
		cal.set(Calendar.YEAR, y);
		cal.set(Calendar.MONTH, m -1);
	}

	/**
	 * カレンダーsetter
	 * @param y 年
	 * @param m 月
	 * @param d 日
	 */
	public void setYmd(int y, int m, int d) {
		cal.set(y, m -1, d);
	}


	/**
	 * カレンダーclear
	 */
	public void calClear() {
		cal.clear();
	}

	/**
	 * <p>祝日取得メソッド</p>
	 * 指定年から国民の祝日を取得
	 */
	public void createHoliday() {
		// 1~12月でfor文をまわす
		for (int i = 1; i <= 12; i++) {
			// その月の日数を取得
			setYm(yyyy, i);
			int days = retMaximumDay();
			// 日数でまわす
			for (int j = 1; j <= days; j++) {
				// 日付をセット
				setYmd(yyyy, i, j);
				// 月曜のみ検索
				if (isDayOfWeek(Calendar.MONDAY)) {
					// 成人の日を特定
					if ((i == SEIJIN_MONTH) && (SEIJIN_DAY_OF_WEEK_IN_MONTH == retDayOfWeekinMonth())) {
						arySyuku[0][0] = convYMD(yyyy, i,j);
					}
					// 海の日を特定
					if ((i == UMI_MONTH) && (UMI_DAY_OF_WEEK_IN_MONTH == retDayOfWeekinMonth())) {
						arySyuku[1][0] = convYMD(yyyy, i, j);
					}
					// 敬老の日を特定
					if ((i == KEIROU_MONTH) && (KEIROU_DAY_OF_WEEK_IN_MONTH == retDayOfWeekinMonth())) {
						arySyuku[2][0] = convYMD(yyyy, i,j);
					}
					// 体育の日を特定
					if ((i == TAIIKU_MONTH) && (TAIIKU_DAY_OF_WEEK_IN_MONTH == retDayOfWeekinMonth())) {
						arySyuku[3][0] = convYMD(yyyy, i,j);
					}
					// 春分の日を特定
					if ((i == SYUNBUN_MONTH)) {
						arySyuku[4][0] = getSyunbunDate(); 
					}
					// 秋分の日を特定
					if ((i == SYUUBUN_MONTH)) {
						arySyuku[5][0] = getSyuubunDate(); 
					}
				}
			}
		}
		// 元日
		arySyuku[6][0] = convYMD(yyyy, GANJITSU_MONTH, GANJITSU_DATE);
		// 建国記念の日
		arySyuku[7][0] = convYMD(yyyy, KENKOKUKINEN_MONTH, KENKOKUKINEN_DATE);
		// 憲法記念日
		arySyuku[8][0] = convYMD(yyyy, KENPOUKINEN_MONTH, KENPOUKINEN_DATE);
		// こどもの日
		arySyuku[9][0] = convYMD(yyyy, KODOMO_MONTH, KODOMO_DATE);
		// 文化の日
		arySyuku[10][0] = convYMD(yyyy, BUNKA_MONTH, BUNKA_DATE);
		// 勤労感謝の日
		arySyuku[11][0] = convYMD(yyyy, KINROUKANSYA_MONTH, KINROUKANSYA_DATE);
		// 天皇誕生日
		arySyuku[12][0] = convYMD(yyyy, TENNOUTANJOU_MONTH, TENNOUTANJOU_DATE);
		// みどりの日は2006年まで4月29日、昭和の日は2007年から導入
		arySyuku[13][0] = convYMD(yyyy, SYOUWA_MONTH, SYOUWA_DATE);
		// 国民の休日は2006年まで5月4日、みどりの日は2007年から導入
		arySyuku[14][0] = convYMD(yyyy, MIDORI_MONTH, MIDORI_DATE);
		// 2007年以前と2007年以降で祝祭日配列を変更
		for (int k = 0; k < arySyuku.length; k++) {
			if (k < arySyukuName.length) {
				arySyuku[k][1] = arySyukuName[k];
			} else if (k == arySyukuName.length) {
				// みどりの日
				if (yyyy <= 2006) {
					arySyuku[k][1] = MIDORI_NAME;
				} else {
					arySyuku[k][1] = SYOUWA_NAME;
				}
			} else if (k == arySyukuName.length + 1) {
				// 国民の休日
				if (yyyy <= 2006) {
					arySyuku[k][1] = KOKUMINNOKYUJITU_NAME;
				} else {
					arySyuku[k][1] = MIDORI_NAME;
				}
			}
		}
	}

	/**
	 * <p>コンバートメソッド</p>
	 * intで入力されたyyyy,mm,ddをStringへ変換
	 * @param y 年
	 * @param m 月
	 * @param d 日
	 * @return yyyymmdd
	 */
	public String convYMD(int y, int m, int d) {
		String syy = "";
		String smm = "";
		String sdd = "";
		String yyyymmdd = "";
		syy = Integer.toString(y);
		if (m < 10) {
			smm = "0" + m;
		} else {
			smm = Integer.toString(m);
		}
		if (d < 10) {
			sdd = "0" + d;
		} else {
			sdd = Integer.toString(d);
		}
		yyyymmdd = syy + "/" + smm + "/" + sdd;
		return yyyymmdd;
	}

	/**
	 * 祝日配列取得メソッド
	 * @return arySyuku
	 */
	public String[][] getArySyuku() { return arySyuku.clone(); }

	/**
	 * 祝日配列取得メソッド
	 * @return aryCombined
	 */
	public String[][] getAryCombinedHoliday() { return aryCombined.clone(); }

	/**
	 *  春分の日の日付を得る(1900〜2099年まで)
	 *  @return yyyymmdd 選択年春分の日
	 */
	public String getSyunbunDate() {
		double date = 21.4471d + (0.242377d * (yyyy-1900)) - Math.floor((yyyy-1900) /4.0d);
		int dd = (int)Math.floor(date);
		return convYMD(yyyy, SYUNBUN_MONTH, dd);
	}

	/**
	 *  秋分の日の日付を得る(1900〜2099年まで)
	 *  @return yyyymmdd 選択年秋分の日
	 */
	public String getSyuubunDate() {
		double date = 23.8896d + (0.242032d * (yyyy-1900)) - Math.floor((yyyy-1900) /4.0d);
		int dd = (int)Math.floor(date);
		return convYMD(yyyy, SYUUBUN_MONTH, dd);
	}

	private enum EnumYoubi {
		SUN("日"), 
		MON("月"), 
		TUE("火"), 
		WED("水"),
		THU("木"),
		FRI("金"),
		SAT("土");

		private String dw;

		private EnumYoubi(String youbi) {
			this.dw = youbi;
		}

		public String toString() {
			return (this.dw);
		}

		static List toArray() {

			ArrayList<String> array = new ArrayList<String>();
			array.add(EnumYoubi.SUN.toString());
			array.add(EnumYoubi.MON.toString());
			array.add(EnumYoubi.TUE.toString());
			array.add(EnumYoubi.WED.toString());
			array.add(EnumYoubi.THU.toString());
			array.add(EnumYoubi.FRI.toString());
			array.add(EnumYoubi.SAT.toString());
			return array; 
		}
	}

	public void setYyyy(int yyyy) { this.yyyy = yyyy; }

}
