# -*- coding: utf-8 -*-

# This file is part of convertdate.
# http://github.com/fitnr/convertdate

# Licensed under the MIT license:
# http://opensource.org/licenses/MIT
# Copyright (c) 2016, fitnr <fitnr@fakeisthenewreal>
import time
import calendar
from math import trunc
from .utils import nth_day_of_month
from . import hebrew, julian

# weekdays
MON = 0
TUE = 1
WED = 2
THU = 3
FRI = 4
SAT = 5
SUN = 6

# months
JAN = 1
FEB = 2
MAR = 3
APR = 4
MAY = 5
JUN = 6
JUL = 7
AUG = 8
SEP = 9
OCT = 10
NOV = 11
DEC = 12


def new_years(year):
    '''Jan 1st'''
    return (year, JAN, 1)


def martin_luther_king_day(year):
    '''third monday in January'''
    return nth_day_of_month(3, MON, JAN, year)


def lincolns_birthday(year):
    '''Feb 12'''
    return (year, FEB, 12)


def valentines_day(year):
    '''feb 14th'''
    return (year, FEB, 14)


def washingtons_birthday(year):
    '''Feb 22'''
    return (year, FEB, 22)


def presidents_day(year):
    '''3rd Monday of Feb'''
    return nth_day_of_month(3, MON, FEB, year)


def pulaski_day(year):
    '''1st monday in March'''
    return nth_day_of_month(1, MON, MAR, year)


def easter(year, church=None):
    '''Calculate Easter in the given church according to the given calendar.'''
    church = church or "western"

    if church == "western":
        return _easter_western(year)

    if church == "orthodox":
        return julian.to_gregorian(*_easter_julian(year))

    if church == "eastern":
        return julian.to_gregorian(*_easter_julian(year, mode="eastern"))

    raise ValueError("Unknown value for 'church'")


def _easter_western(year):
    '''Calculate western easter'''
    # formula taken from http://aa.usno.navy.mil/faq/docs/easter.html
    c = trunc(year / 100)
    n = year - 19 * trunc(year / 19)

    k = trunc((c - 17) / 25)

    i = c - trunc(c / 4) - trunc((c - k) / 3) + (19 * n) + 15
    i = i - 30 * trunc(i / 30)
    i = i - trunc(i / 28) * (1 - trunc(i / 28) * trunc(29 / (i + 1)) * trunc((21 - n) / 11))

    j = year + trunc(year / 4) + i + 2 - c + trunc(c / 4)
    j = j - 7 * trunc(j / 7)

    l = i - j

    month = 3 + trunc((l + 40) / 44)
    day = l + 28 - 31 * trunc(month / 4)

    date = (year, int(month), int(day))

    return date


def _easter_julian(year, mode="dionysian"):
    '''Calculate Easter for the orthodox and eastern churches in the Julian calendar.'''
    # Uses Meeus's Julian algorithm.
    meton = (year % 19)
    b = year % 4
    c = year % 7
    d = (19 * meton + 15) % 30
    if mode == "eastern" and meton == 0:
        d = d + 1
    e = (2 * b + 4 * c - d + 6) % 7
    fmj = 113 + d  # Easter full moon (days after -92 March)
    dmj = fmj + e + 1  # Easter Sunday (days after -92 March)
    esmj = trunc(dmj / 31)  # month of Easter Sunday
    esdj = (dmj % 31) + 1  # day of Easter Sunday

    return year, esmj, esdj


def may_day(year):
    return (year, MAY, 1)


def mothers_day(year):
    '''2nd Sunday in May'''
    return nth_day_of_month(2, SUN, MAY, year)


def memorial_day(year):
    '''last Monday in May'''
    return nth_day_of_month(0, MON, MAY, year)


def fathers_day(year):
    '''3rd Sunday in June'''
    return nth_day_of_month(3, SUN, JUN, year)


def flag_day(year):
    '''June 14th'''
    return (year, JUN, 14)


def independence_day(year, observed=None):
    '''July 4th'''
    day = 4

    if observed:
        if calendar.weekday(year, JUL, 4) == SAT:
            day = 3

        if calendar.weekday(year, JUL, 4) == SUN:
            day = 5

    return (year, JUL, day)


def labor_day(year):
    '''first Monday in Sep'''
    return nth_day_of_month(1, MON, SEP, year)


def columbus_day(year, country='usa'):
    '''in USA: 2nd Monday in Oct
       Elsewhere: Oct 12'''
    if country == 'usa':
        return nth_day_of_month(2, MON, OCT, year)
    else:
        return (year, OCT, 12)


def halloween(year):
    '''Oct 31'''
    return (year, OCT, 31)


def election_day(year):
    '''1st Tues in Nov'''
    return nth_day_of_month(1, TUE, NOV, year)


def veterans_day(year):
    '''Nov 11'''
    return (year, NOV, 11)


def rememberance_day(year):
    return veterans_day(year)


def armistice_day(year):
    return veterans_day(year)


def thanksgiving(year, country='usa'):
    '''USA: last Thurs. of November, Canada: 2nd Mon. of October'''
    if country == 'usa':
        if year in [1940, 1941]:
            return nth_day_of_month(3, THU, NOV, year)
        elif year == 1939:
            return nth_day_of_month(4, THU, NOV, year)
        else:
            return nth_day_of_month(0, THU, NOV, year)

    if country == 'canada':
        return nth_day_of_month(2, MON, OCT, year)


def christmas_eve(year):
    '''24th of December'''
    return (year, DEC, 24)


def christmas(year):
    '''25th of December'''
    return (year, DEC, 25)


def new_years_eve(year):
    '''Dec 31st'''
    return (year, DEC, 31)

# Jewish holidays begins the sunset before the first (secular) day of the holiday
# With the eve option set, the day of this sunset is returned
# without the option, the (secular) day is returned


def hanukkah(year, eve=None):
    year, month, day = hebrew.to_jd_gregorianyear(year, hebrew.KISLEV, 25)
    if eve:
        day = day - 1
    return year, month, day


def purim(year, eve=None):
    if not hebrew.leap(year + hebrew.HEBREW_YEAR_OFFSET):
        year, month, day = hebrew.to_jd_gregorianyear(year, hebrew.ADAR, 14)
    else:
        year, month, day = hebrew.to_jd_gregorianyear(year, hebrew.VEADAR, 14)
    if eve:
        day = day - 1
    return year, month, day


def rosh_hashanah(year, eve=None):
    year, month, day = hebrew.to_jd_gregorianyear(year, hebrew.TISHRI, 1)
    if eve:
        day = day - 1
    return year, month, day


def yom_kippur(year, eve=None):
    year, month, day = hebrew.to_jd_gregorianyear(year, hebrew.TISHRI, 10)
    if eve:
        day = day - 1
    return year, month, day


def passover(year, eve=None):
    year, month, day = hebrew.to_jd_gregorianyear(year, hebrew.NISAN, 15)
    if eve:
        day = day - 1
    return year, month, day


def shavuot(year, eve=None):
    year, month, day = hebrew.to_jd_gregorianyear(year, hebrew.SIVAN, 6)
    if eve:
        day = day - 1
    return year, month, day


def sukkot(year, eve=None):
    year, month, day = hebrew.to_jd_gregorianyear(year, hebrew.TISHRI, 15)
    if eve:
        day = day - 1
    return year, month, day


# Mexican holidays


def dia_constitucion(year, observed=True):
    if observed:
        return nth_day_of_month(1, MON, FEB, year)
    else:
        return (year, FEB, 5)


def natalicio_benito_juarez(year, observed=True):
    if observed:
        return nth_day_of_month(3, MON, MAR, year)
    else:
        return (year, MAR, 21)


def dia_independencia(year):
    return year, SEP, 16


def dia_revolucion(year):
    return nth_day_of_month(3, MON, NOV, year)


class Holidays(object):

    def __init__(self, year=None):
        self.year = year or time.localtime().tm_year

    def set_year(self, year):
        self.year = year

    def __repr__(self):
        return 'Holidays({})'.format(self.year)

    # the holidays...
    @property
    def christmas(self):
        return christmas(self.year)

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

    @property
    def thanksgiving(self, country='usa'):
        return thanksgiving(self.year, country)

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

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

    @property
    def independence_day(self):
        return independence_day(self.year, observed=True)

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

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

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

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

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

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

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

    @property
    def columbus_day(self, country='usa'):
        return columbus_day(self.year, country)

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

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

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

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

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

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

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

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

    @property
    def hanukkah(self):
        return hanukkah(self.year, eve=False)

    @property
    def purim(self):
        return purim(self.year, eve=False)

    @property
    def rosh_hashanah(self):
        return rosh_hashanah(self.year, eve=False)

    @property
    def yom_kippur(self):
        return yom_kippur(self.year, eve=False)

    @property
    def passover(self):
        return passover(self.year, eve=False)

    @property
    def shavuot(self):
        return shavuot(self.year, eve=False)

    @property
    def sukkot(self):
        return sukkot(self.year, eve=False)

    @property
    def dia_constitucion(self):
        return dia_constitucion(self.year, observed=True)

    @property
    def natalicio_benito_juarez(self):
        return natalicio_benito_juarez(self.year, observed=True)

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

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


if __name__ == '__main__':
    holiday = Holidays(time.localtime().tm_year)