// Copyright (C) 2007, 2013 panacoran <panacoran@users.sourceforge.jp>
// 
// This program is part of Protra.
//
// Protra 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 3 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, see <http://www.gnu.org/licenses/>.
// 
// $Id$

using System;

namespace Protra.Lib
{
    /// <summary>
    /// 休場日に関するクラス
    /// </summary>
    public class Calendar
    {
        /// <summary>
        /// 指定された日付に市場が開いているかを返す。
        /// </summary>
        /// <param name="d">日付を指定する。</param>
        /// <returns></returns>
        public static bool IsMarketOpen(DateTime d)
        {
            // 土日
            if (d.DayOfWeek == DayOfWeek.Saturday ||
                d.DayOfWeek == DayOfWeek.Sunday)
                return false;
            // 年末年始
            if ((d.Month == 12 && d.Day == 31) ||
                (d.Month == 1 && d.Day <= 3))
                return false;
            return !IsHoliday(d);
        }

        /// <summary>
        /// 指定された日付が週の最後の開場日かを返す。
        /// </summary>
        /// <param name="d"></param>
        /// <returns></returns>
        public static bool IsLastOpenDateOfWeek(DateTime d)
        {
            for (d = d.AddDays(1); d.DayOfWeek < DayOfWeek.Saturday; d = d.AddDays(1))
                if (IsMarketOpen(d))
                    return false;
            return true;
        }

        private static bool IsHoliday(DateTime d)
        {
            // 動かない休日
            if ((d.Month == 1 && d.Day == 1) ||
                (d.Month == 2 && d.Day == 11) ||
                (d.Month == 4 && d.Day == 29) ||
                (d.Month == 5 && (d.Day >= 3 && d.Day <= 5)) ||
                (d.Month == 11 && (d.Day == 3 || d.Day == 23)) ||
                (d.Month == 12 && d.Day == 23))
                return true;
            // 春分と秋分(1980〜2099年に対応)
            if (d.Month == 3 &&
// ReSharper disable PossibleLossOfFraction
                d.Day == (int)(20.8431 + 0.242194 * (d.Year - 1980) - (d.Year - 1980) / 4))
// ReSharper restore PossibleLossOfFraction
                return true;
            if (d.Month == 9 &&
// ReSharper disable PossibleLossOfFraction
                d.Day == (int)(23.2488 + 0.242194 * (d.Year - 1980) - (d.Year - 1980) / 4))
// ReSharper restore PossibleLossOfFraction
                return true;
            // ハッピーマンデー
            if (d.Year < 2000)
            {
                if ((d.Month == 1 && d.Day == 15) ||
                    (d.Month == 10 && d.Day == 10))
                    return true;
            }
            else
            {
                if ((d.Month == 1 || d.Month == 10) &&
                    (d.Day >= 8 && d.Day <= 14) && d.DayOfWeek == DayOfWeek.Monday)
                    return true;
            }
            if (d.Year < 2003)
            {
                if ((d.Month == 7 && d.Day == 20) ||
                    (d.Month == 9 && d.Day == 15))
                    return true;
            }
            else
            {
                if ((d.Month == 7 || d.Month == 9) &&
                    (d.Day >= 15 && d.Day <= 21) && d.DayOfWeek == DayOfWeek.Monday)
                    return true;
                // シルバーウィーク
                if (d.Month == 9 && (d.Day == 21 || d.Day == 22) && d.DayOfWeek == DayOfWeek.Tuesday)
                    return IsHoliday(d.AddDays(1));
            }
            // 5月4日が国民の祝日になったので、振替休日が二日以上繰り越す。
            if (d.Year > 2007 && d.Month == 5 && d.Day == 6)
                return d.DayOfWeek >= DayOfWeek.Monday &&
                       d.DayOfWeek <= DayOfWeek.Wednesday;
            // 振り替え休日
            if (d.DayOfWeek == DayOfWeek.Monday)
                return IsHoliday(d.AddDays(-1));
            return false;
        }
    }
}