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


import unittest
from timelinelib.calendar.gregorian.timetype import GregorianTimeType
from timelinelib.calendar.gregorian.time import GregorianDelta
from timelinelib.calendar.gregorian.gregoriandatetime import GregorianDateTime
from timelinelib.calendar.generic.timepicker.datemodifier import DateModifier
from timelinelib.calendar.pharaonic.timetype.timetype import PharaonicTimeType
from timelinelib.calendar.pharaonic.time import PharaonicDelta
from timelinelib.calendar.pharaonic.pharaonicdatetime import PharaonicDateTime


class DescribeDateModifierBase(unittest.TestCase):

    def assert_test_cases(self, header, function, test_cases):
        failed = False
        for doc, input_value, expected_result in test_cases:
            result =  function(input_value)
            if result != expected_result:
                print(f'{header}')
                print(f'Test failed: {doc}')
                print(f'  Input   : {input_value}')
                print(f'  Expected: {expected_result}')
                print(f'  Got     : {result}')
                failed = True
        if failed:
            self.fail()

    def set_up(self, date_modifier):
        self.max_year = date_modifier._date_time.from_time(date_modifier._time_type.get_max_time()).year
        self.min_year = date_modifier._date_time.from_time(date_modifier._time_type.get_min_time()).year
        self.max_time = date_modifier._date_time.from_time(date_modifier._time_type.get_max_time()).to_date_tuple()
        self.min_time = date_modifier._date_time.from_time(date_modifier._time_type.get_min_time()).to_date_tuple()
        self.max_time_minus_one_day = date_modifier._date_time.from_time(
            date_modifier._time_type.get_max_time() - date_modifier._delta.from_days(1)).to_date_tuple()
        self.min_time_plus_one_day = date_modifier._date_time.from_time(
            date_modifier._time_type.get_min_time() + date_modifier._delta.from_days(1)).to_date_tuple()


class DescribeDateModifier(DescribeDateModifierBase):

    def test_increment_year(self):
        test_cases = (
            ("1. Normal year increment ok.", (2022, 1, 1), (2023, 1, 1)),
            ("2. Max year can't be incremented.", (self.max_year, 1, 1), (self.max_year, 1, 1)),
            ("3. Max time can't be year incremented.", self.max_time, self.max_time),
            ("4. Increment year from leap day ok.", (2020, 2, 29), (2021, 2, 28)),
        )
        self.assert_test_cases("test_increment_year", self.date_modifier.increment_year, test_cases)

    def test_decrement_year(self):
        test_cases = (
            ("1. Normal year decrement ok.", (2022, 1, 1), (2021, 1, 1)),
            ("2. Min year can't be decremented.", (self.min_year, 1, 1), self.min_time),
            ("3. Min time can't be year decremented.", self.min_time, self.min_time),
            ("4. Decrement from leap year ok.", (2020, 2, 29), (2019, 2, 28)),
            ("5. Decrement to leap year ok.", (2021, 2, 28), (2020, 2, 28)),
        )
        self.assert_test_cases("test_decrement_year", self.date_modifier.decrement_year, test_cases)

    def test_increment_month(self):
        test_cases = (
            ("1. Normal month increment ok.", (2022, 3, 1), (2022, 4, 1)),
            ("2. Max month increment ok.", (2022, 12, 1), (2023, 1, 1)),
            ("3. Max month can't be incremented on max year.", (self.max_year, 12, 1), self.max_time),
            ("4. Max time can't be month incremented.", self.max_time, self.max_time),
            ("5. Increment from leap month ok.", (2020, 2, 29), (2020, 3, 29)),
            ("6. Increment to leap month ok.", (2020, 1, 31), (2020, 2, 29)),
        )
        self.assert_test_cases("test_increment_month", self.date_modifier.increment_month, test_cases)

    def test_decrement_month(self):
        test_cases = (
            ("1. Normal month decrement ok.", (2022, 12, 4), (2022, 11, 4)),
            ("2. Min month decrement ok.", (2022, 1, 1), (2021, 12, 1)),
            ("3. Min month can't be decremented on min year.", (self.min_year, 1, 1), self.min_time),
            ("4. Min time can't be month decremented.", self.min_time, self.min_time),
            ("5. Decrement to leap month ok.", (2020, 3, 31), (2020, 2, 29)),
            ("6. Decrement to non leap month ok.", (2021, 3, 31), (2021, 2, 28)),
        )
        self.assert_test_cases("test_decrement_month", self.date_modifier.decrement_month, test_cases)

    def test_increment_day(self):
        test_cases = (
            ("1. Normal day increment ok.", (2022, 12, 4), (2022, 12, 5)),
            ("2. Max day increment ok.", (2022, 11, 30), (2022, 12, 1)),
            ("3. Max day in max month increment ok.", (2022, 12, 31), (2023, 1, 1)),
            ("4. Max day in max month in max year can't be incremented.", (self.max_year, 12, 5), self.max_time),
            ("5. Max time can't be day incremented.", self.max_time, self.max_time),
            ("6. Max time minus one day can be incremented", self.max_time_minus_one_day, self.max_time),
            ("7. Increment to leap day ok.", (2020, 2, 28), (2020, 2, 29)),
            ("8. Increment from leap day ok.", (2020, 2, 29), (2020, 3, 1)),
        )
        self.assert_test_cases("test_increment_day", self.date_modifier.increment_day, test_cases)

    def test_decrement_day(self):
        test_cases = (
            ("1. Normal day decrement ok.", (2022, 12, 4), (2022, 12, 3)),
            ("2. Min day decrement ok.", (2022, 12, 1), (2022, 11, 30)),
            ("3. Min day in max month decrement ok.", (2022, 12, 1), (2022, 11, 30)),
            ("4. Min day in min month decrement ok.", (2021, 1, 1), (2020, 12, 31)),
            ("5. Min day in min month in min year can't be decremented.", (self.min_year, 1, 1), self.min_time),
            ("6. Min time can't be day decremented.", self.min_time, self.min_time),
            ("7. Min time plus one day can be decremented", self.min_time_plus_one_day, self.min_time),
            ("8. Decrement to leap day ok.", (2020, 3, 1), (2020, 2, 29)),
            ("9. Decrement from leap day ok.", (2020, 2, 29), (2020, 2, 28)),
        )
        self.assert_test_cases("test_decrement_day", self.date_modifier.decrement_day, test_cases)

    def setUp(self):
        self.date_modifier = DateModifier(GregorianTimeType(), GregorianDelta, GregorianDateTime)
        self.set_up(self.date_modifier)


class DescribePharaonicDateModifier(DescribeDateModifierBase):

    def test_increment_year(self):
        test_cases = (
            ("1. Normal year increment ok.", (2022, 1, 1), (2023, 1, 1)),
            ("2. Max year can't be incremented.", (self.max_year, 1, 1), (self.max_year, 1, 1)),
            ("3. Max time can't be year incremented.", self.max_time, self.max_time),
        )
        self.assert_test_cases("test_increment_year", self.date_modifier.increment_year, test_cases)

    def test_decrement_year(self):
        test_cases = (
            ("1. Normal year decrement ok.", (2022, 1, 1), (2021, 1, 1)),
            ("2. Min year can't be decremented.", (self.min_year, 1, 1), self.min_time),
            ("3. Min time can't be year decremented.", self.min_time, self.min_time),
        )
        self.assert_test_cases("test_decrement_year", self.date_modifier.decrement_year, test_cases)

    def test_increment_month(self):
        test_cases = (
            ("1. Normal month increment ok.", (2022, 12, 4), (2022, 13, 4)),
            ("2. Max month increment ok.", (2022, 13, 1), (2023, 1, 1)),
            ("3. Max month can't be incremented on max year.", (self.max_year, 13, 1), self.max_time),
            ("4. Max time can't be month incremented.", self.max_time, self.max_time),
        )
        self.assert_test_cases("test_increment_month", self.date_modifier.increment_month, test_cases)

    def test_decrement_month(self):
        test_cases = (
            ("1. Normal month decrement ok.", (2022, 12, 4), (2022, 11, 4)),
            ("2. Min month decrement ok.", (2022, 1, 1), (2021, 13, 1)),
            ("3. Min month can't be decremented on min year.", (self.min_year, 1, 1), self.min_time),
            ("4. Min time can't be month decremented.", self.min_time, self.min_time),
        )
        self.assert_test_cases("test_decrement_month", self.date_modifier.decrement_month, test_cases)

    def test_increment_day(self):
        test_cases = (
            ("1. Normal day increment ok.", (2022, 12, 4), (2022, 12, 5)),
            ("2. Max day increment ok.", (2022, 12, 30), (2022, 13, 1)),
            ("3. Max day in max month increment ok.", (2022, 13, 5), (2023, 1, 1)),
            ("4. Max day in max month in max year can't be incremented.", (self.max_year, 13, 5), self.max_time),
            ("5. Max time can't be day incremented.", self.max_time, self.max_time),
            ("6. Max time minus one day can be incremented", self.max_time_minus_one_day, self.max_time)
        )
        self.assert_test_cases("test_increment_day", self.date_modifier.increment_day, test_cases)

    def test_decrement_day(self):
        test_cases = (
            ("1. Normal day decrement ok.", (2022, 12, 4), (2022, 12, 3)),
            ("2. Min day decrement ok.", (2022, 12, 1), (2022, 11, 30)),
            ("3. Min day in max month decrement ok.", (2022, 13, 1), (2022, 12, 30)),
            ("4. Min day in min month decrement ok.", (2021, 1, 1), (2020, 13, 5)),
            ("5. Min day in min month in min year can't be decremented.", (self.min_year, 1, 1), self.min_time),
            ("6. Min time can't be day decremented.", self.min_time, self.min_time),
            ("7. Min time plus one day can be decrmented", self.min_time_plus_one_day, self.min_time)
        )
        self.assert_test_cases("test_decrement_day", self.date_modifier.decrement_day, test_cases)

    def setUp(self):
        self.date_modifier = DateModifier(PharaonicTimeType(), PharaonicDelta, PharaonicDateTime, max_month=13)
        self.set_up(self.date_modifier)
