# -*- coding: utf-8 -*-
'''
    Convert between Gregorian/Julian Day and Comte's Positivist calendar.
    The Positivist calendar has 13 months and one or two festival days.
    Festival days are given as the fourteenth month.
    The Gregorian date 1789-01-01 is Positivist 0001-01-01.
'''
# 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 calendar import isleap
from math import floor

from . import gregorian
from .data import positivist as data

# Positivist calendar has 13 28-day months and one festival day

EPOCH = 2374479.5

YEAR_EPOCH = 1789

DAYS_IN_YEAR = 365

MONTHS = (
    'Moses',
    'Homer',
    'Aristotle',
    'Archimedes',
    'Caesar',
    'Saint Paul',
    'Charlemagne',
    'Dante',
    'Gutenberg',
    'Shakespeare',
    'Descartes',
    'Frederic',
    'Bichat',
    '',
)


def legal_date(year, month, day):
    '''Checks if a given date is a legal positivist date'''
    try:
        assert year >= 1
        assert 0 < month <= 14
        assert 0 < day <= 28
        if month == 14:
            if isleap(year + YEAR_EPOCH - 1):
                assert day <= 2
            else:
                assert day == 1

    except AssertionError as err:
        raise ValueError("Invalid Positivist date: ({}, {}, {})".format(year, month, day)) from err

    return True


def to_jd(year, month, day):
    '''Convert a Positivist date to Julian day count.'''
    legal_date(year, month, day)
    gyear = year + YEAR_EPOCH - 1

    return (
        gregorian.EPOCH
        - 1
        + (365 * (gyear - 1))
        + floor((gyear - 1) / 4)
        + (-floor((gyear - 1) / 100))
        + floor((gyear - 1) / 400)
        + (month - 1) * 28
        + day
    )


def from_jd(jd):
    '''Convert a Julian day count to Positivist date.'''
    try:
        assert jd >= EPOCH
    except AssertionError as err:
        raise ValueError('Invalid Julian day') from err

    depoch = floor(jd - 0.5) + 0.5 - gregorian.EPOCH

    quadricent = floor(depoch / gregorian.INTERCALATION_CYCLE_DAYS)
    dqc = depoch % gregorian.INTERCALATION_CYCLE_DAYS

    cent = floor(dqc / gregorian.LEAP_SUPPRESSION_DAYS)
    dcent = dqc % gregorian.LEAP_SUPPRESSION_DAYS

    quad = floor(dcent / gregorian.LEAP_CYCLE_DAYS)
    dquad = dcent % gregorian.LEAP_CYCLE_DAYS

    yindex = floor(dquad / gregorian.YEAR_DAYS)
    year = (
        quadricent * gregorian.INTERCALATION_CYCLE_YEARS
        + cent * gregorian.LEAP_SUPPRESSION_YEARS
        + quad * gregorian.LEAP_CYCLE_YEARS
        + yindex
    )

    if yindex == 4:
        yearday = 365
        year = year - 1

    else:
        yearday = (
            depoch
            - quadricent * gregorian.INTERCALATION_CYCLE_DAYS
            - cent * gregorian.LEAP_SUPPRESSION_DAYS
            - quad * gregorian.LEAP_CYCLE_DAYS
            - yindex * gregorian.YEAR_DAYS
        )

    month = floor(yearday / 28)

    return (year - YEAR_EPOCH + 2, month + 1, int(yearday - (month * 28)) + 1)


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 dayname(year, month, day):
    """
    Give the name of the month and day for a given date.

    Returns:
        tuple month_name, day_name
    """
    legal_date(year, month, day)

    yearday = (month - 1) * 28 + day

    if isleap(year + YEAR_EPOCH - 1):
        dname = data.DAY_NAMES_LEAP[yearday - 1]
    else:
        dname = data.DAY_NAMES[yearday - 1]

    return MONTHS[month - 1], dname


def weekday(day):
    """
    Gives the weekday (0=Monday) of a positivist month and day.
    Note that the festival month does not have a day.
    """
    return (day % 7) - 1


def festival(month, day):
    """
    Gives the festival day for a month and day.
    Returns None if inapplicable.
    """
    return data.FESTIVALS.get((month, day))
