# NOTE: t must be INT!!!
import time
import datetime
import warnings

try:
    from tzlocal import get_localzone
    LOCAL_ZONE = get_localzone()
except:  # except all problems...
    warnings.warn(
        'Please install or fix tzlocal library (pip install tzlocal) in order to make Date object work better. Otherwise I will assume DST is in effect all the time'
    )

    class LOCAL_ZONE:
        @staticmethod
        def dst(*args):
            return 1


from js2py.base import MakeError
CUM = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365)
msPerDay = 86400000
msPerYear = int(86400000 * 365.242)
msPerSecond = 1000
msPerMinute = 60000
msPerHour = 3600000
HoursPerDay = 24
MinutesPerHour = 60
SecondsPerMinute = 60
NaN = float('nan')
LocalTZA = -time.timezone * msPerSecond


def DaylightSavingTA(t):
    if t is NaN:
        return t
    try:
        return int(
            LOCAL_ZONE.dst(datetime.datetime.utcfromtimestamp(
                t // 1000)).seconds) * 1000
    except:
        warnings.warn(
            'Invalid datetime date, assumed DST time, may be inaccurate...',
            Warning)
        return 1
        #raise MakeError('TypeError', 'date not supported by python.datetime. I will solve it in future versions')


def GetTimeZoneName(t):
    return time.tzname[DaylightSavingTA(t) > 0]


def LocalToUTC(t):
    return t - LocalTZA - DaylightSavingTA(t - LocalTZA)


def UTCToLocal(t):
    return t + LocalTZA + DaylightSavingTA(t)


def Day(t):
    return t // 86400000


def TimeWithinDay(t):
    return t % 86400000


def DaysInYear(y):
    if y % 4:
        return 365
    elif y % 100:
        return 366
    elif y % 400:
        return 365
    else:
        return 366


def DayFromYear(y):
    return 365 * (y - 1970) + (y - 1969) // 4 - (y - 1901) // 100 + (
        y - 1601) // 400


def TimeFromYear(y):
    return 86400000 * DayFromYear(y)


def YearFromTime(t):
    guess = 1970 - t // 31556908800  # msPerYear
    gt = TimeFromYear(guess)
    if gt <= t:
        while gt <= t:
            guess += 1
            gt = TimeFromYear(guess)
        return guess - 1
    else:
        while gt > t:
            guess -= 1
            gt = TimeFromYear(guess)
        return guess


def DayWithinYear(t):
    return Day(t) - DayFromYear(YearFromTime(t))


def InLeapYear(t):
    y = YearFromTime(t)
    if y % 4:
        return 0
    elif y % 100:
        return 1
    elif y % 400:
        return 0
    else:
        return 1


def MonthFromTime(t):
    day = DayWithinYear(t)
    leap = InLeapYear(t)
    if day < 31:
        return 0
    day -= leap
    if day < 59:
        return 1
    elif day < 90:
        return 2
    elif day < 120:
        return 3
    elif day < 151:
        return 4
    elif day < 181:
        return 5
    elif day < 212:
        return 6
    elif day < 243:
        return 7
    elif day < 273:
        return 8
    elif day < 304:
        return 9
    elif day < 334:
        return 10
    else:
        return 11


def DateFromTime(t):
    mon = MonthFromTime(t)
    day = DayWithinYear(t)
    return day - CUM[mon] - (1 if InLeapYear(t) and mon >= 2 else 0) + 1


def WeekDay(t):
    # 0 == sunday
    return (Day(t) + 4) % 7


def msFromTime(t):
    return t % 1000


def SecFromTime(t):
    return (t // 1000) % 60


def MinFromTime(t):
    return (t // 60000) % 60


def HourFromTime(t):
    return (t // 3600000) % 24


def MakeTime(hour, Min, sec, ms):
    # takes PyJs objects and returns t
    if not (hour.is_finite() and Min.is_finite() and sec.is_finite()
            and ms.is_finite()):
        return NaN
    h, m, s, milli = hour.to_int(), Min.to_int(), sec.to_int(), ms.to_int()
    return h * 3600000 + m * 60000 + s * 1000 + milli


def MakeDay(year, month, date):
    # takes PyJs objects and returns t
    if not (year.is_finite() and month.is_finite() and date.is_finite()):
        return NaN
    y, m, dt = year.to_int(), month.to_int(), date.to_int()
    y += m // 12
    mn = m % 12
    d = DayFromYear(y) + CUM[mn] + dt - 1 + (1 if DaysInYear(y) == 366
                                             and mn >= 2 else 0)
    return d  # ms per day


def MakeDate(day, time):
    return 86400000 * day + time


def TimeClip(t):
    if t != t or abs(t) == float('inf'):
        return NaN
    if abs(t) > 8.64 * 10**15:
        return NaN
    return int(t)
