"""HuntLibrary.py

Schedule and calendar calculations for determining state of the HL A11 performance space and environs.
"""

import datetime

#================================================================
# Define dates in which the building is on a holiday schedule.
holidays = [
    # datetime.date(2024,  9,  2),   # Labor Day
    # datetime.date(2023, 10, 17),   # day library closed during Fall Break
    datetime.date(2024, 11,  5),   # Tue Democracy Day
    datetime.date(2024, 11, 27),   # Wed of Thanksgiving break
    datetime.date(2024, 11, 28),   # Thu of Thanksgiving break
    datetime.date(2024, 11, 29),   # Fri of Thanksgiving break
    ]

# Define the time interval for the KF class itself.
class_start_time = datetime.time(12,00)  # 12:00 PM
class_end_time   = datetime.time(13,50)  #  1:50 PM

# Define a set of specific demo intervals.  Each entry is a tuple (start, end).
demos = [

    # FrIDeATe events
    (datetime.datetime(2024, 11, 15, 17, 00), datetime.datetime(2024, 11, 15, 18, 00)),

]
#================================================================

def datetime_string(date):
    """Return a string representation of a datetime object.  This includes
    redundant date text for readability."""
    return date.strftime("%Y-%m-%d %a %b %d %H:%M:%S %p")

def date_string(date):
    """Return a string representation of a date object.  This includes
    redundant text for readability."""
    return date.strftime("%Y-%m-%d %a %b %d")

def is_datetime_demo(now):
    """Returns true if the specified time is during a demo interval.
    :param now: datetime object (i.e. both date and time)
    """
    for start, end in demos:
        if now >= start and now <= end:
            return True
    return False

#================================================================

def datetime_properties(now):
    """Given a datetime object, return a dictionary with the time and a number
    of properties useful for time-based policy decisions.
    """

    now_date = now.date()         # extract a date object
    now_time = now.time()         # extract a time object

    weekday = now_date.weekday()  # Monday is 0, Sunday is 6

    hour    = now_time.hour
    minute  = now_time.minute

    result = dict()
    result['time'] = now
    result['string'] = datetime_string(now)

    # true if the day is on a weekend
    result['is_weekend'] =  (weekday == 5) or (weekday == 6)

    # true if the date falls on a campus holiday
    result['is_campus_holiday'] = (now_date in holidays)

    # true if the time falls during the regular business day on a non-holiday
    result['is_business_hours'] = (not result['is_weekend']
                                   and (not result['is_campus_holiday'])
                                   and (hour >= 8) and (hour < 18))

    # true if the date and time fall during class
    result['is_class_time'] = (((weekday == 1) or (weekday == 3)) # Tue or Thu
                               and (not result['is_campus_holiday'])
                               and (now_time >= class_start_time)
                               and (now_time < class_end_time))

    # true if a special demo time
    result['is_demo_time'] = is_datetime_demo(now)

    # true if time and date are reasonable for active operation
    result['is_show_time'] = (result['is_demo_time'] or
                              (result['is_business_hours'] and (not result['is_class_time']) and (minute > 49)))

    return result

#================================================================
def current_time_properties():
    """Return a dictionary with the current time and a number of properties useful for time-based policy decisions.
    """
    now = datetime.datetime.now() # datetime() object with both date and time
    return datetime_properties(now)

#================================================================
# The following section is run when this is loaded as a script.
if __name__ == "__main__":
    print("Holidays:")
    for date in holidays:
        print("  " + date_string(date))

    print("\nDemo list:")
    for start, end in demos:
        print("  %s  to   %s" % (datetime_string(start), datetime_string(end)))

    def test(sample):
        props = datetime_properties(sample)
        for key, value in props.items():
            print("  %20s: %s" % (key, value))
        print()

    print("\nCurrent time properties:")
    test(datetime.datetime.now())

    print("\nTest times:")
    test(datetime.datetime(2023, 10, 18, 16, 0))

    tests_failed = 0
    def dt_assert(sample, showtime):
        props = datetime_properties(sample)
        if props['is_show_time'] != showtime:
            print("Assertion error for", sample, showtime)
            tests_failed += 1

    # test validity against expected results

    dt_assert(datetime.datetime(2023,  8, 28, 17, 59), True )   # day time
    dt_assert(datetime.datetime(2023,  8, 28, 18,  0), False)   # after normal hours
    dt_assert(datetime.datetime(2023,  8, 29, 14,  1), False)   # past the hour
    dt_assert(datetime.datetime(2023,  9,  3, 14, 55), False)   # weekend
    dt_assert(datetime.datetime(2023, 10, 18, 11, 55), True )   # day time
    dt_assert(datetime.datetime(2023, 10, 18, 18, 55), False)   # evening
    dt_assert(datetime.datetime(2023, 10, 19,  7, 55), False)   # early morning
    dt_assert(datetime.datetime(2023, 10, 19, 10, 55), True )   # mid morning
    dt_assert(datetime.datetime(2023, 10, 20,  7, 55), False)   # early morning
    dt_assert(datetime.datetime(2023, 10, 20, 17, 55), True )   # day time
    dt_assert(datetime.datetime(2023, 10, 20, 18, 55), False)   # evening

    # holiday tests
    # dt_assert(datetime.datetime(2023, 10, 17, 11, 55), False)   # library closed
    # dt_assert(datetime.datetime(2023, 10, 17, 14,  1), False)   # library closed

    # demo tests
    # dt_assert(datetime.datetime(2023, 10, 18, 16,  0), True )   # demo time
    # dt_assert(datetime.datetime(2023, 10, 19,  8, 30), True )   # demo time
    # dt_assert(datetime.datetime(2023, 10, 20,  8, 30), True )   # demo time
    # dt_assert(datetime.datetime(2023, 10, 20, 17, 30), True )   # demo time

    # dt_assert(datetime.datetime(2024, 1, 9, 10, 30), True )   # demo time
    # dt_assert(datetime.datetime(2024, 1, 9, 11, 30), True )   # demo time
    # dt_assert(datetime.datetime(2024, 1, 9, 12, 30), True )   # demo time

    if tests_failed == 0:
        print("All tests passed.")
