import icalendar
import utils
import time
import datetime
import dateutil.rrule
import operator

CACHE_EXPIRY = 15*60
URL = "http://www.google.com/calendar/ical/loadingreadyrun.com_72jmf1fn564cbbr84l048pv1go%40group.calendar.google.com/public/basic.ics"

@utils.throttle(CACHE_EXPIRY)
def get_calendar_data():
	ical = utils.http_request(URL)
	return icalendar.Calendar.from_ical(ical)

def get_next_event(after=None, all=False):
	cal_data = get_calendar_data()
	events = []
	if after is None:
		after = datetime.datetime.now(datetime.timezone.utc)
	for ev in cal_data.subcomponents:
		if isinstance(ev, icalendar.Event):
			event_name = str(ev['summary'])
			event_time = ev['dtstart'].dt
			if not isinstance(event_time, datetime.datetime):
				# ignore full-day events
				continue
			# Report episodes that are either in the first half, or started less than an hour ago
			# Whichever is shorter
			cutoff_delay = (ev['dtend'].dt - ev['dtstart'].dt) / 2
			if cutoff_delay > datetime.timedelta(hours=1):
				cutoff_delay = datetime.timedelta(hours=1)
			event_time_cutoff = event_time + cutoff_delay
			if 'rrule' in ev:
				rrule = dateutil.rrule.rrulestr(ev['rrule'].to_ical().decode('utf-8'), dtstart=event_time_cutoff)
				### MASSIVE HACK ALERT
				_apply_monkey_patch(rrule)
				### END MASSIVE HACK ALERT
				event_time_cutoff = rrule.after(after)
				if event_time_cutoff is None:
					continue
				event_time = event_time_cutoff - cutoff_delay
			if event_time_cutoff > after:
				events.append((event_name, event_time))
	if all:
		events.sort(key=operator.itemgetter(1))
		return events
	if events:
		event_name, event_time = min(events, key=operator.itemgetter(1))
		event_wait = (event_time - after).total_seconds()
		return event_name, event_time, event_wait
	else:
		return None, None, None

def _apply_monkey_patch(rrule):
	"""
	The processing in dateutil.rrule is not properly timezone-aware, mostly because
	Python's timezone handling is far from ideal. In particular, it will claim that,
	for instance, "2014-03-06 19:00:00 PST" is "2014-03-13 19:00:00 PST", when in fact
	the correct answer is "2014-03-13 19:00:00 PDT", as daylight savings has started.

	Notably, the time from the original time to the time 1 week later is not 7*24 hours.

	We monkey-patch the rrule class so that all the datetimes that come out of it are
	re-localised, so that the naive hour/minute values are preserved, but the DST flag
	is changed. This means that the corrected dates are what is seen by rrule.after(),
	when it does comparisons using the dates, so the correct results should come out.

	This is a hack that messes with the internals of the library, and as such is far
	from a good idea. It was written against dateutil 2.2, and no guarantees that it
	will work with any other version of that library. A better solution would be
	appreciated, even if it means ditching dateutil for a better library that does
	what we want it to here.
	"""
	if rrule._freq > dateutil.rrule.DAILY:
		return

	import functools
	old_iter = rrule._iter
	@functools.wraps(old_iter)
	def new_iter(*args, **kwargs):
		for dt in old_iter(*args, **kwargs):
			yield dt.tzinfo.localize(dt.replace(tzinfo=None))
	rrule._iter = new_iter

@@ -2016,16 +2016,29 @@
 nstance,
+ a week after
  %222014-0
@@ -2085,17 +2085,18 @@
 00 PST%22,
- 
+%0A%09
 when in 
@@ -2099,18 +2099,17 @@
  in fact
-%0A%09
+ 
 the corr
@@ -2168,17 +2168,18 @@
  savings
- 
+%0A%09
 has star
@@ -2268,16 +2268,43 @@
 24 hours
+%0A%09when calculated correctly
 .%0A%0A%09We m
@@ -2990,58 +2990,8 @@
 %22%22%22%0A
-%09if rrule._freq %3E dateutil.rrule.DAILY:%0A%09%09return%0A%0A
 %09imp
@@ -3088,16 +3088,431 @@
 wargs):%0A
+%09%09# If the rule is Daily/Weekly/Monthly/etc, then we want to keep the naive%0A%09%09# date/time values, eg 1 day after %227PM PST%22 is still %227pm PDT%22%0A%09%09# even though that's only 23 hours difference.%0A%09%09# If the rule is Hourly/etc, then we want to use the timezone-aware time,%0A%09%09# eg one hour after %221:30AM PST%22 should be %223:30AM PDT%22, as that is%0A%09%09# a time gap of one actual hour.%0A%09%09if rrule._freq %3C= dateutil.rrule.DAILY:%0A%09
 %09%09for dt
@@ -3542,16 +3542,17 @@
 wargs):%0A
+%09
 %09%09%09yield
@@ -3596,16 +3596,98 @@
 =None))%0A
+%09%09else:%0A%09%09%09for dt in old_iter(*args, **kwargs):%0A%09%09%09%09yield dt.tzinfo.normalize(dt)%0A
 %09rrule._

Tweak comments and some extra cases