# -*- coding: utf-8 -*-
# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018  Rickard Lindberg, Roger Lindberg
#
# This file is part of Timeline.
#
# Timeline 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.
#
# Timeline 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 Timeline.  If not, see <http://www.gnu.org/licenses/>.

"""
Contains the GregorianDateTime class and static functions related
to the class.

:doc:`Tests are found here <unit_calendar_gregorian_gregorian>`.
"""


from timelinelib.calendar.generic.datetime import DateTime
from timelinelib.calendar.gregorian.time import GregorianDelta
from timelinelib.calendar.gregorian.time import GregorianTime
from timelinelib.calendar.gregorian.gregorian2julian import gregorian_ymd_to_julian_day
from timelinelib.calendar.gregorian.julian2gregorian import julian_day_to_gregorian_ymd
from timelinelib.calendar.gregorian.monthnames import abbreviated_name_of_month
from timelinelib.calendar.gregorian.weekdaynames import abbreviated_name_of_weekday


class GregorianDateTime(DateTime):
    """ """

    def __init__(self, year, month, day, hour, minute, second):
        """ """
        if not self.is_valid(year, month, day):
            raise ValueError("Invalid gregorian date %s-%s-%s" % (year, month, day))
        DateTime.__init__(self, year, month, day, hour, minute, second)

    @classmethod
    def TimeClass(cls):
        return GregorianTime

    @classmethod
    def julian_day_to_ymd_fn(cls):
        return julian_day_to_gregorian_ymd

    @classmethod
    def ymd_to_julian_day_fn(cls):
        return gregorian_ymd_to_julian_day

    @property
    def week_number(self):
        """ """

        def monday_week_1(year):
            jan_4 = GregorianDateTime.from_ymd(year, 1, 4).to_time()
            return jan_4 - GregorianDelta.from_days(jan_4.day_of_week)

        def days_between(end, start):
            return end.julian_day - start.julian_day

        def days_since_monday_week_1(time):
            year = GregorianDateTime.from_time(time).year
            diff = days_between(end=time, start=monday_week_1(year + 1))
            if diff >= 0:
                return diff
            diff = days_between(end=time, start=monday_week_1(year))
            if diff >= 0:
                return diff
            diff = days_between(end=time, start=monday_week_1(year - 1))
            if diff >= 0:
                return diff
            raise ValueError("should not end up here")

        return days_since_monday_week_1(self.to_time()) // 7 + 1

    @property
    def is_leap_year(self):
        return self.year_is_leap_year(self.year)

    def days_in_month(self):
        """ """
        return self.nbr_of_days_in_month(self.year, self.month)

    @property
    def days_in_week(self):
        return 7

    @property
    def days_in_year(self):
        if self.is_leap_year:
            return 366
        else:
            return 365

    @classmethod
    def months_in_year(cls):
        return 12

    @property
    def abbreviated_month_name(self):
        return abbreviated_name_of_month(self.month)

    @property
    def abbreviated_weekday_name(self):
        return abbreviated_name_of_weekday(self.to_time().day_of_week)

    @staticmethod
    def year_is_leap_year(year):
        """ """
        return year % 4 == 0 and (year % 400 == 0 or not year % 100 == 0)

    @classmethod
    def nbr_of_days_in_month(cls, year, month):
        """ """
        if month in [4, 6, 9, 11]:
            return 30
        if month in [1, 3, 5, 7, 8, 10, 12]:
            return 31
        if cls.year_is_leap_year(year):
            return 29
        return 28
