# -*- 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>
from math import trunc
from . import gregorian
from .utils import jwday, monthcalendarhelper

EPOCH = 347995.5
HEBREW_YEAR_OFFSET = 3760

# Hebrew months
NISAN = 1
IYYAR = 2
SIVAN = 3
TAMMUZ = 4
AV = 5
ELUL = 6
TISHRI = 7
HESHVAN = 8
KISLEV = 9
TEVETH = 10
SHEVAT = 11
ADAR = 12
VEADAR = 13


def leap(year):
    # Is a given Hebrew year a leap year ?
    return (((year * 7) + 1) % 19) < 7


def year_months(year):
    '''How many months are there in a Hebrew year (12 = normal, 13 = leap)'''
    if leap(year):
        return 13
    else:
        return 12


def delay_1(year):
    '''Test for delay of start of new year and to avoid'''
    # Sunday, Wednesday, and Friday as start of the new year.
    months = trunc(((235 * year) - 234) / 19)
    parts = 12084 + (13753 * months)
    day = trunc((months * 29) + parts / 25920)

    if ((3 * (day + 1)) % 7) < 3:
        day += 1

    return day


def delay_2(year):
    '''Check for delay in start of new year due to length of adjacent years'''

    last = delay_1(year - 1)
    present = delay_1(year)
    next_ = delay_1(year + 1)

    if next_ - present == 356:
        return 2
    elif present - last == 382:
        return 1
    else:
        return 0


def year_days(year):
    '''How many days are in a Hebrew year ?'''
    return to_jd(year + 1, 7, 1) - to_jd(year, 7, 1)


def month_days(year, month):
    '''How many days are in a given month of a given year'''
    if month > 13:
        raise ValueError("Incorrect month index")

    # First of all, dispose of fixed-length 29 day months
    if month in (IYYAR, TAMMUZ, ELUL, TEVETH, VEADAR):
        return 29

    # If it's not a leap year, Adar has 29 days
    if month == ADAR and not leap(year):
        return 29

    # If it's Heshvan, days depend on length of year
    if month == HESHVAN and (year_days(year) % 10) != 5:
        return 29

    # Similarly, Kislev varies with the length of year
    if month == KISLEV and (year_days(year) % 10) == 3:
        return 29

    # Nope, it's a 30 day month
    return 30


def to_jd(year, month, day):
    months = year_months(year)
    jd = EPOCH + delay_1(year) + delay_2(year) + day + 1

    if month < 7:
        for mon in range(7, months + 1):
            jd += month_days(year, mon)

        for mon in range(1, month):
            jd += month_days(year, mon)
    else:
        for mon in range(7, month):
            jd += month_days(year, mon)

    return int(jd) + 0.5


def from_jd(jd):
    jd = trunc(jd) + 0.5
    count = trunc(((jd - EPOCH) * 98496.0) / 35975351.0)
    year = count - 1
    i = count
    while jd >= to_jd(i, 7, 1):
        i += 1
        year += 1

    if jd < to_jd(year, 1, 1):
        first = 7
    else:
        first = 1

    month = i = first
    while jd > to_jd(year, i, month_days(year, i)):
        i += 1
        month += 1

    day = int(jd - to_jd(year, month, 1)) + 1
    return (year, month, day)


def to_jd_gregorianyear(gregorianyear, hebrew_month, hebrew_day):
    # gregorian year is either 3760 or 3761 years less than hebrew year
    # we'll first try 3760 if conversion to gregorian isn't the same
    # year that was passed to this method, then it must be 3761.

    for y in (gregorianyear + HEBREW_YEAR_OFFSET, gregorianyear + HEBREW_YEAR_OFFSET + 1):
        jd = to_jd(y, hebrew_month, hebrew_day)
        gd = gregorian.from_jd(jd)
        if gd[0] == gregorianyear:
            break
        else:
            gd = None

    if not gd:  # should never occur, but just incase...
        raise ValueError("Could not determine gregorian year")

    # tuple: (y, m, d)
    return (gd[0], gd[1], gd[2])


def from_gregorian(year, month, day):
    return from_jd(gregorian.to_jd(year, month, day))


def to_gregorian(year, month, day):
    return gregorian.from_jd(to_jd(year, month, day))


def monthcalendar(year, month):
    start_weekday = jwday(to_jd(year, month, 1))
    monthlen = month_days(year, month)
    return monthcalendarhelper(start_weekday, monthlen)