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


from timelinelib.calendar.coptic.time import CopticTime
from timelinelib.test.cases.unit import UnitTestCase
import timelinelib.calendar.coptic.copticdatetime as coptic


class DescribeCopticDateTimeCreation(UnitTestCase):

    @staticmethod
    def test_accepts_valid_dates():
        coptic.CopticDateTime(2013, 1, 1, 0, 0, 0)
        coptic.CopticDateTime(2016, 13, 5, 0, 0, 0)

    @staticmethod
    def test_accepts_leap_day_dates():
        coptic.CopticDateTime(2015, 13, 6, 0, 0, 0)
        coptic.CopticDateTime(2019, 13, 6, 0, 0, 0)
        coptic.CopticDateTime(-4, 13, 6, 0, 0, 0)

    def test_rejects_invalid_month_dates(self):
        self.assertRaises(ValueError, coptic.CopticDateTime, 2013, 0, 1, 0, 0, 0)
        self.assertRaises(ValueError, coptic.CopticDateTime, 2013, 14, 1, 0, 0, 0)

    def test_rejects_invalid_day_dates(self):
        self.assertRaises(ValueError, coptic.CopticDateTime, 2013, 1, 0, 0, 0, 0)
        self.assertRaises(ValueError, coptic.CopticDateTime, 2013, 1, 31, 0, 0, 0)

    def test_rejects_invalid_leap_day_dates(self):
        self.assertRaises(ValueError, coptic.CopticDateTime, 2013, 13, 6, 0, 0, 0)


class DescribeCopticReplace(UnitTestCase):

    def test_can_replace(self):
        g = coptic.CopticDateTime(2013, 7, 12, 10, 16, 12)
        self.assertEqual(g.replace(year=1990), coptic.CopticDateTime(1990, 7, 12, 10, 16, 12))
        self.assertEqual(g.replace(month=6), coptic.CopticDateTime(2013, 6, 12, 10, 16, 12))
        self.assertEqual(g.replace(year=1990, month=6), coptic.CopticDateTime(1990, 6, 12, 10, 16, 12))

    def test_rejects_invalid_replace(self):
        g = coptic.CopticDateTime(2013, 7, 12, 10, 16, 12)
        self.assertRaises(ValueError, g.replace, month=13)


class CopticConversionsSpec(UnitTestCase):

    def test_can_convert_from_timeline_time_to_coptic(self):
        self.assertEqual(
            coptic.CopticDateTime.from_time(CopticTime(julian_day=0, seconds=0)),
            coptic.CopticDateTime(-4996, 5, 5, 0, 0, 0))
        self.assertEqual(
            coptic.CopticDateTime.from_time(CopticTime(julian_day=1, seconds=0)),
            coptic.CopticDateTime(-4996, 5, 6, 0, 0, 0))

    def test_can_convert_from_coptic_to_timeline_time(self):
        self.assertEqual(
            coptic.CopticDateTime(-4996, 5, 5, 0, 0, 0).to_time(),
            CopticTime(julian_day=0, seconds=0))
        self.assertEqual(
            coptic.CopticDateTime(-4996, 5, 6, 0, 0, 0).to_time(),
            CopticTime(julian_day=1, seconds=0))

    def test_roundtrip_julian_day_conversions(self):
        for julian_day in range(100):
            (year, month, day) = coptic.julian_day_to_coptic_ymd(julian_day)
            roundtrip = coptic.coptic_ymd_to_julian_day(year, month, day)
            self.assertEqual(roundtrip, julian_day)

    def test_roundtrip_coptic_dates_conversions(self):
        dates = [
            (2013, 1, 1),
            (2013, 1, 30),
        ]
        for coptic_date in dates:
            (year, month, day) = coptic_date
            julian_day = coptic.coptic_ymd_to_julian_day(year, month, day)
            roundtrip = coptic.julian_day_to_coptic_ymd(julian_day)
            self.assertEqual(roundtrip, coptic_date)


class DescribeCopticPrimitives(UnitTestCase):

    def test_days_in_month(self):
        for month in range(1, 13):
            self.assertEqual(30, coptic.CopticDateTime.nbr_of_days_in_month(2013, month))
        self.assertEqual(5, coptic.CopticDateTime.nbr_of_days_in_month(2014, 13))
        self.assertEqual(30, coptic.CopticDateTime.from_ymd(2014, 1, 1).days_in_month())
        self.assertEqual(5, coptic.CopticDateTime.from_ymd(2014, 13, 1).days_in_month())
        self.assertEqual(30, coptic.CopticDateTime.from_ymd(2015, 1, 1).days_in_month())
        self.assertEqual(6, coptic.CopticDateTime.from_ymd(2015, 13, 1).days_in_month())
        self.assertEqual(30, coptic.CopticDateTime.from_ymd(-3, 1, 1).days_in_month())
        self.assertEqual(5, coptic.CopticDateTime.from_ymd(-3, 13, 1).days_in_month())
        self.assertEqual(30, coptic.CopticDateTime.from_ymd(-4, 1, 1).days_in_month())
        self.assertEqual(6, coptic.CopticDateTime.from_ymd(-4, 13, 1).days_in_month())

    def test_is_leap_year(self):
        self.assertFalse(coptic.CopticDateTime.year_is_leap_year(2013))
        self.assertFalse(coptic.CopticDateTime.year_is_leap_year(1900))
        self.assertTrue(coptic.CopticDateTime.year_is_leap_year(2007))
        self.assertTrue(coptic.CopticDateTime.year_is_leap_year(1899))
        self.assertTrue(coptic.CopticDateTime.year_is_leap_year(-4))
        self.assertTrue(coptic.CopticDateTime.year_is_leap_year(-8))

    def test_week_number(self):
        def assert_is_week(date_tuple, w):
            (y, m, d) = date_tuple
            date = coptic.CopticDateTime.from_ymd(y, m, d)
            self.assertEqual(date.week_number, w)
        assert_is_week((2015, 13, 6), 1)
        assert_is_week((1733, 1, 1), 52)
        assert_is_week((1735, 1, 1), 1)
        assert_is_week((1735, 1, 5), 1)
        assert_is_week((1735, 1, 27), 4)
        assert_is_week((1734, 13, 4), 52)
        assert_is_week((1734, 13, 3), 52)
        assert_is_week((1735, 1, 1), 1)
        assert_is_week((1735, 7, 1), 26)
        assert_is_week((-4, 7, 1), 26)

    def test_nbr_of_days_in_year(self):
        self.assertEqual(365, coptic.CopticDateTime.from_ymd(2014, 1, 1).days_in_year)
        self.assertEqual(366, coptic.CopticDateTime.from_ymd(2015, 1, 1).days_in_year)
        self.assertEqual(365, coptic.CopticDateTime.from_ymd(-3, 1, 1).days_in_year)
        self.assertEqual(366, coptic.CopticDateTime.from_ymd(-4, 1, 1).days_in_year)

    def test_days_in_week(self):
        self.assertEqual(7, coptic.CopticDateTime.from_ymd(2014, 1, 1).days_in_week)
        self.assertEqual(7, coptic.CopticDateTime.from_ymd(2015, 2, 10).days_in_week)
        self.assertEqual(7, coptic.CopticDateTime.from_ymd(-3, 3, 20).days_in_week)
        self.assertEqual(7, coptic.CopticDateTime.from_ymd(-4, 5, 30).days_in_week)

    def test_abbreviated_week_name(self):
        self.assertEqual(_('Pes'), coptic.CopticDateTime.from_ymd(2014, 1, 1).abbreviated_weekday_name)
        self.assertEqual(_('Pt'), coptic.CopticDateTime.from_ymd(2014, 1, 11).abbreviated_weekday_name)
        self.assertEqual(_('Tk'), coptic.CopticDateTime.from_ymd(2014, 1, 21).abbreviated_weekday_name)
        self.assertEqual(_('Pef'), coptic.CopticDateTime.from_ymd(2014, 2, 1).abbreviated_weekday_name)
        self.assertEqual(_('Psa'), coptic.CopticDateTime.from_ymd(2014, 2, 11).abbreviated_weekday_name)
        self.assertEqual(_('Psh'), coptic.CopticDateTime.from_ymd(2014, 2, 21).abbreviated_weekday_name)
        self.assertEqual(_('Pso'), coptic.CopticDateTime.from_ymd(2014, 3, 1).abbreviated_weekday_name)
        self.assertEqual(_('Pes'), coptic.CopticDateTime.from_ymd(2014, 3, 11).abbreviated_weekday_name)

    def test_abbreviated_month_name(self):
        self.assertEqual(_('I Akhet'), coptic.CopticDateTime.from_ymd(2014, 1, 1).abbreviated_month_name)
        self.assertEqual(_('II Akhet'), coptic.CopticDateTime.from_ymd(2014, 2, 1).abbreviated_month_name)
        self.assertEqual(_('III Akhet'), coptic.CopticDateTime.from_ymd(2014, 3, 1).abbreviated_month_name)
        self.assertEqual(_('IV Akhet'), coptic.CopticDateTime.from_ymd(2014, 4, 1).abbreviated_month_name)
        self.assertEqual(_('I Peret'), coptic.CopticDateTime.from_ymd(2014, 5, 1).abbreviated_month_name)
        self.assertEqual(_('II Peret'), coptic.CopticDateTime.from_ymd(2014, 6, 1).abbreviated_month_name)
        self.assertEqual(_('III Peret'), coptic.CopticDateTime.from_ymd(2014, 7, 1).abbreviated_month_name)
        self.assertEqual(_('IV Peret'), coptic.CopticDateTime.from_ymd(2014, 8, 1).abbreviated_month_name)
        self.assertEqual(_('I Shemu'), coptic.CopticDateTime.from_ymd(2014, 9, 1).abbreviated_month_name)
        self.assertEqual(_('II Shemu'), coptic.CopticDateTime.from_ymd(2014, 10, 1).abbreviated_month_name)
        self.assertEqual(_('III Shemu'), coptic.CopticDateTime.from_ymd(2014, 11, 1).abbreviated_month_name)
        self.assertEqual(_('IV Shemu'), coptic.CopticDateTime.from_ymd(2014, 12, 1).abbreviated_month_name)
        self.assertEqual(_('- -'), coptic.CopticDateTime.from_ymd(2014, 13, 1).abbreviated_month_name)
