# 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/>.


def overrides_base_class_function(fn):
    """A decorator function for documenting overrides."""
    def wrapper(*args, **kwargs):
        return fn(*args, **kwargs)
    return wrapper


class DateTime:
    """Base class/Interface for all different DateTime classes."""

    BC = _("BC")

    def __init__(self, year, month, day, hour, minute, second):
        """ """
        self._year = year
        self._month = month
        self._day = day
        self._hour = hour
        self._minute = minute
        self._second = second

    @classmethod
    def months_in_year(cls):
        raise NotImplementedError(f"The months_in_year() has not yet been defined in {cls.__name__}")

    @classmethod
    def nbr_of_days_in_month(cls, year, month):
        raise NotImplementedError(f"The nbr_of_days_in_month() has not yet been defined in {cls.__name__}")

    @classmethod
    def is_valid(cls, year, month, day):
        return 1 <= month <= cls.months_in_year() and 1 <= day <= cls.nbr_of_days_in_month(year, month)

    def __repr__(self):
        """ """
        return f"{self.__class__.__name__}" + ("<%d-%02d-%02d %02d:%02d:%02d>" % self.to_tuple())

    def __eq__(self, other):
        """ """
        return isinstance(other, self.__class__) and self.to_tuple() == other.to_tuple()

    def __ne__(self, other):
        """ """
        return not (self == other)

    @classmethod
    def TimeClass(cls):
        raise NotImplementedError(f"The TimeClass() has not yet been defined in {cls.__name__}")

    @classmethod
    def julian_day_to_ymd_fn(cls):
        raise NotImplementedError(f"The julian_day_to_ymd_fn() has not yet been defined in {cls.__name__}")

    @classmethod
    def ymd_to_julian_day_fn(cls):
        raise NotImplementedError(f"The ymd_to_julian_day_fn() has not yet been defined in {cls.__name__}")

    @property
    def year(self):
        return self._year

    @property
    def formatted_year(self):
        if self._year <= 0:
            return "%d %s" % ((1 - self._year), self.BC)
        else:
            return str(self._year)

    @property
    def month(self):
        return self._month

    @property
    def day(self):
        return self._day

    @property
    def hour(self):
        return self._hour

    @property
    def minute(self):
        return self._minute

    @property
    def second(self):
        return self._second

    def is_bc(self):
        return self._year <= 0

    def is_first_day_in_year(self):
        """ """
        return (self._month == 1 and
                self._day == 1 and
                self._hour == 0 and
                self._minute == 0 and
                self._second == 0)

    def is_first_of_month(self):
        """ """
        return (self._day == 1 and
                self._hour == 0 and
                self._minute == 0 and
                self._second == 0)

    @staticmethod
    def is_valid_time(hour, minute, second):
        """Default implementation."""
        return 0 <= hour < 24 and 0 <= minute < 60 and 0 <= second < 60

    @classmethod
    def from_time(cls, time):
        """ """
        (year, month, day) = cls.julian_day_to_ymd_fn()(time.julian_day)
        (hour, minute, second) = time.get_time_of_day()
        return cls(year, month, day, hour, minute, second)

    def to_time(self):
        days = self.ymd_to_julian_day_fn()(self._year, self._month, self._day)
        seconds = self._hour * 60 * 60 + self._minute * 60 + self._second
        return self.TimeClass()(days, seconds)

    @classmethod
    def from_ymd(cls, year, month, day):
        """ """
        return cls(year, month, day, 0, 0, 0)

    def to_tuple(self):
        """ """
        return self._year, self._month, self._day, self._hour, self._minute, self._second

    def to_date_tuple(self):
        """ """
        return self._year, self._month, self._day

    def to_time_tuple(self):
        """ """
        return self._hour, self._minute, self._second

    def replace(self, year=None, month=None, day=None, hour=None, minute=None, second=None):
        if year is None:
            year = self._year
        if month is None:
            month = self._month
        if day is None:
            day = self._day
        if hour is None:
            hour = self._hour
        if minute is None:
            minute = self._minute
        if second is None:
            second = self._second
        return self.__class__(
            year,
            month,
            day,
            hour,
            minute,
            second
        )
