/*
 * Copyright (C) 2008-2009 GLAD!! (ITO Yoshiichi)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package jp.sourceforge.glad.calendar.holiday;

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

import jp.sourceforge.glad.calendar.ISOCalendar;
import jp.sourceforge.glad.calendar.Instant;

/**
 * 国ごとの祝日の一覧。
 * 
 * @author GLAD!!
 */
public abstract class CountryHolidays {

    // ---- fields

    /** 国コード (ISO 3166-1 alpha-2) */
    final String country;

    /** 祝日の一覧 */
    final List<Holiday> holidays;

    // ---- constructors

    /**
     * オブジェクトを構築します。
     * 
     * @param country  国コード (ISO 3166-1 alpha-2)
     * @param holidays 祝日の一覧
     */
    CountryHolidays(String country, List<Holiday> holidays) {
        this.country = country;
        this.holidays = Collections.unmodifiableList(holidays);
    }

    // ---- flyweight

    /**
     * デフォルトの国の祝日の一覧を返します。
     * 
     * @return 祝日一覧
     */
    public static CountryHolidays getInstance() {
        return Holidays.getInstance().getDefaultHolidays();
    }

    /**
     * 指定された国の祝日一覧を返します。
     * 
     * @param country 国コード (ISO 3166-1 alpha-2)
     * @return 祝日一覧
     */
    public static CountryHolidays getInstance(String country) {
        return Holidays.getInstance().getCountryHolidays(country);
    }

    // ---- accessors

    /**
     * 国コードを返します。
     * 
     * @return 国コード (ISO 3166-1 alpha-2)
     */
    public String getCountry() {
        return country;
    }

    /**
     * 祝日の一覧を返します。
     * 
     * @return 祝日の一覧
     */
    public List<Holiday> getHolidays() {
        return holidays;
    }

    // ---- other methods

    /**
     * 指定年の祝日一覧を返します。
     * 
     * @param year 年 (西暦)
     * @return 祝日一覧
     */
    public abstract List<Holiday> getHolidays(int year);

    /**
     * 指定年月日の祝日を返します。
     * 
     * @param year  年 (西暦)
     * @param month 月
     * @param day   日
     * @return 祝日 (祝日でない場合は null)
     */
    public abstract Holiday getHoliday(int year, int month, int day);

    /**
     * 指定日の祝日を返します。
     * 
     * @param timeInMillis 通算ミリ秒
     * @param zone タイムゾーン
     * @return 祝日 (祝日でない場合は null)
     */
    public Holiday getHoliday(long timeInMillis, TimeZone zone) {
        return getHoliday0(new ISOCalendar(timeInMillis, zone));
    }

    /**
     * 指定日の祝日を返します。
     * 
     * @param calendar Calendar
     * @return 祝日 (祝日でない場合は null)
     */
    public Holiday getHoliday(Calendar calendar) {
        return getHoliday0(new ISOCalendar(calendar));
    }

    /**
     * 指定日の祝日を返します。
     * 
     * @param date Date
     * @return 祝日 (祝日でない場合は null)
     */
    public Holiday getHoliday(Date date) {
        return getHoliday0(new ISOCalendar(date));
    }

    /**
     * 指定日の祝日を返します。
     * 
     * @param instant 時点
     * @return 祝日 (祝日でない場合は null)
     */
    public Holiday getHoliday(Instant instant) {
        return getHoliday0(toISOCalendar(instant));
    }

    /**
     * 指定日の祝日を返します。
     * 
     * @param calendar ISOCalendar
     * @return 祝日 (祝日でない場合は null)
     */
    Holiday getHoliday0(ISOCalendar calendar) {
        return getHoliday(
                calendar.getYear(),
                calendar.getMonth(),
                calendar.getDay());
    }

    ISOCalendar toISOCalendar(Instant instant) {
        if (instant instanceof ISOCalendar) {
            return (ISOCalendar) instant;
        } else {
            return new ISOCalendar(instant);
        }
    }

    /**
     * 指定年月日が祝日か判定します。
     * 
     * @param year  年 (西暦)
     * @param month 月
     * @param day   日
     * @return 祝日の場合は true
     */
    public boolean isHoliday(int year, int month, int day) {
        return getHoliday(year, month, day) != null;
    }

    /**
     * 指定日が祝日か判定します。
     * 
     * @param timeInMillis 通算ミリ秒
     * @param zone タイムゾーン
     * @return 祝日の場合は true
     */
    public boolean isHoliday(long timeInMillis, TimeZone zone) {
        return getHoliday(timeInMillis, zone) != null;
    }

    /**
     * 指定日が祝日か判定します。
     * 
     * @param calendar Calendar
     * @return 祝日の場合は true
     */
    public boolean isHoliday(Calendar calendar) {
        return getHoliday(calendar) != null;
    }

    /**
     * 指定日が祝日か判定します。
     * 
     * @param date Date
     * @return 祝日の場合は true
     */
    public boolean isHoliday(Date date) {
        return getHoliday(date) != null;
    }

    /**
     * 指定日が祝日か判定します。
     * 
     * @param instant Instant
     * @return 祝日の場合は true
     */
    public boolean isHoliday(Instant instant) {
        return getHoliday(instant) != null;
    }

}
