#!/usr/bin/env python

import StringIO
import serial
import time
import sys
import gregorian_calendar
import calendar
calendar.setfirstweekday(calendar.SUNDAY)

# http://msdn2.microsoft.com/en-us/library/aa210899(office.11).aspx

# The interface between Python and COM consists of two discrete parts: the pythoncom Python extension module and the win32com Python package. Collectively,

import win32com.client

olFolderCalendar = 9

olRecursDaily = 0
olRecursWeekly = 1
olRecursMonthly = 2
olRecursMonthNth = 3
olRecursYearly = 5 # ???
olRecursYearNth = 6 # ???

olSunday    = 1
olMonday    = 2
olTuesday   = 4
olWednesday = 8
olThursday  = 16
olFriday    = 32
olSaturday  = 64

olAppointmentItem = 1

def create_appointment(**kwargs):
	""" don't forget to call "Save()" and/or "Display(true)" (modal) on the returned appointment.
		known kwargs are:
			Start
			Duration
			Subject
			Body
			Location
			ReminderMinutesBeforeStart
			ReminderSet = True
	"""
	Outlook_session = win32com.client.Dispatch("Outlook.Application")
	MAPI_Outlook = Outlook_session.GetNamespace("MAPI")
	calendar_folder = MAPI_Outlook.GetDefaultFolder(olFolderCalendar)
	#for item in calendar_folder.Items:

	appointment = Outlook_session.CreateItem(olAppointmentItem)

	for key, value in kwargs.items():
		if isinstance(value, str):
			value = value.decode("utf-8")

		setattr(appointment, key, value)

	# appointment.Start = #....#
	# appointment.Duration = 60
	# appointment.Body = ...

	#appointment.Save()
	return appointment

def get_next_times_for_appointment(item, low_cutoff):
		DayOfWeekMask = None
		start = gregorian_calendar.gregorian_from_OLE_time(item.Start)
		t = gregorian_calendar.make_time(start) 
		assert(map(int, time.strftime("%Y,%m,%d,%H,%M,%S", time.localtime(t)).split(",")) == start)

		if item.IsRecurring:
			item_recurrence = item.GetRecurrencePattern()
			RecurrenceType = item_recurrence.RecurrenceType

			RecurrenceInterval = item_recurrence.Interval

			RecurrenceIntervalUnit = 0
			if RecurrenceType == olRecursDaily:
				assert(RecurrenceInterval == 1)
				RecurrenceIntervalUnit = 2
			elif RecurrenceType == olRecursMonthly:
				# no. assert(RecurrenceInterval == 1)
				RecurrenceIntervalUnit = 1
			elif RecurrenceType == olRecursWeekly:
				# TODO .DayOfWeekMask
				# no. assert(RecurrenceInterval == 1)
				# convert to days.
				RecurrenceIntervalUnit = 2
				if RecurrenceInterval == 0:
					RecurrenceInterval = 1
				RecurrenceInterval = RecurrenceInterval * 7
				DayOfWeekMask = item_recurrence.DayOfWeekMask
			elif RecurrenceType == olRecursYearly:
				#print item.Start, item.Subject, item.End
				#print(item_recurrence.PatternStartDate)
				# RecurrenceInterval seem to be months anyway (!)
				RecurrenceIntervalUnit = 1

			#print item.Start, item.Subject, RecurrenceType, RecurrenceInterval, RecurrenceIntervalUnit

			RecurrenceStart = gregorian_calendar.gregorian_from_OLE_time(item_recurrence.PatternStartDate)[ : 3] + start[3 : ]

			if item_recurrence.NoEndDate:
				RecurrenceEnd = gregorian_calendar.future_end_limit
				RecurrenceEnd_time = gregorian_calendar.make_time(gregorian_calendar.future_end_limit)
			else:
				RecurrenceEnd = gregorian_calendar.gregorian_from_OLE_time(item_recurrence.PatternEndDate)
				RecurrenceEnd_time = gregorian_calendar.make_time(RecurrenceEnd)

			#print "RECE", RecurrenceEnd, item_recurrence.PatternEndDate, RecurrenceInterval 

			RecurrenceStart_time = gregorian_calendar.make_time(RecurrenceStart)
			RecurrenceStart_JDN = gregorian_calendar.julian_day_number(RecurrenceStart)
			# Occurrences, NoEndDate
			if RecurrenceInterval == 0:
				print("ignoring strange outlook entry", RecurrenceType, item.Subject, item.Start)
		else:
			RecurrenceType = 0
			RecurrenceInterval = 0
			RecurrenceIntervalUnit = 0
			item_recurrence = None
			RecurrenceStart = None
			RecurrenceEnd = None
			RecurrenceStart_time = None
			RecurrenceEnd_time = None

		# TODO check whether "RequiredAttendees" contains me.
		# Sensitivity, ReminderSet 

		end = gregorian_calendar.gregorian_from_OLE_time(item.End)

		if item.IsRecurring:
			# TODO calculate a "current" starting time for recurring items? increment_gregorian divide, 
			# x = (low_cutoff - RecurrenceStart) // RecurrenceInterval
			# RecurrenceStart + x * RecurrenceInterval

			# (low_cutoff_JDN - RecurrenceStart_JDN) // RecurrenceInterval

			# julian_day_number
			# low_cutoff_JDN

			# slow but sure way:

			#previous_start = start
			previous_start = [low_cutoff[0], low_cutoff[1], low_cutoff[2], start[3], start[4], start[5]]
			#b = repr(item.Start) == "<PyTime:28.05.2010 14:00:00>"
			#if b: print item, start, "recurring"

			start = RecurrenceStart # for recurrence every weekday, seems to be the first sunday.
			exceptions = {}
			for exception in item_recurrence.Exceptions:
				original_date = gregorian_calendar.gregorian_from_OLE_time(exception.OriginalDate)
				original_date_exception_key = gregorian_calendar.format_date_ISO8601(original_date[ : 3]) # year, month, day.
				if exception.Deleted:
					exceptions[original_date_exception_key] = False # delete mark.
				else:
					exceptions[original_date_exception_key] = exception.AppointmentItem

			#print "exceptions", exceptions

			# TODO properly handle DayOfWeekMask, if there. RecurrenceInterval is then usually 7 or 14.
			next_occurrences = []
			for recurrence_index in range(5): # next 5 recurrences:
				#print "\"", item.Subject, "\" recurring from ", start, "->", RecurrenceIntervalUnit, RecurrenceInterval
				while start <= list(previous_start):
					#if b: print "REC", RecurrenceInterval, RecurrenceIntervalUnit, start
					#print "REC", start, low_cutoff[:], start < low_cutoff[:]
					start = gregorian_calendar.increment_gregorian(start, RecurrenceIntervalUnit, RecurrenceInterval)

					# item.Duration # minutes

					# TODO multi-day events?
					end = [start[0], start[1], start[2], end[3], end[4], end[5]]
					start = [start[0], start[1], start[2], previous_start[3], previous_start[4], previous_start[5]]

				previous_start = start				

				#print "-> transformed to ", start, end

				start_day = [start[0], start[1], start[2]]

				start_day_exception_key = gregorian_calendar.format_date_ISO8601(start_day)

				exception = exceptions.get(start_day_exception_key)

				if exception is not None: # an exception exists.
					if exception != False: # not deleted.
						real_start = gregorian_calendar.gregorian_from_OLE_time(exception.Start)
						real_end = gregorian_calendar.gregorian_from_OLE_time(exception.End)
						#print "exception %s - %s..." % (real_start, real_end)
						next_occurrences.append((real_start, real_end))
				else:
					next_occurrences.append((start, end))
		else:
			next_occurrences = [(start, end)]

		return item, next_occurrences

def get_next_appointments(low_cutoff_time, cutoff_time):
	"""
	get the next (upcoming) appointments coming up between "low_cutoff_time" and "cutoff_time".
	returns: multiple yield <Item:http://msdn2.microsoft.com/en-us/library/aa210899(office.11).aspx>, [(start, end), (start, end), ...]
	"""
	global olFolderCalendar

	low_cutoff_time = low_cutoff_time - 30 # seconds.
	low_cutoff = time.localtime(low_cutoff_time)[ : len(gregorian_calendar.future_end_limit)]
	
	#low_cutoff_JDN = gregorian_calendar.julian_day_number(low_cutoff)

	Outlook_session = win32com.client.Dispatch("Outlook.Application")

	MAPI_Outlook = Outlook_session.GetNamespace("MAPI")
	calendar_folder = MAPI_Outlook.GetDefaultFolder(olFolderCalendar)

	#If myrecurrencepattern.RecurrenceType = 1 then
	#'62 is Monday through Friday added together
	#If myrecurrencepattern.DayOfWeekMask = 62 then
	#'Then it's a week day recurrence.
	#End If
	#End If

	#Set objLableColl = olCalendar.Items.Restrict("[Beschriftung] = Privat") 
	for item in calendar_folder.Items:
		start = gregorian_calendar.gregorian_from_OLE_time(item.Start)
		item_start_time = gregorian_calendar.make_time(start) 
		if item_start_time < low_cutoff_time:
			if item.IsRecurring:
				item_recurrence = item.GetRecurrencePattern()
				if item_recurrence.NoEndDate:
					RecurrenceEnd = gregorian_calendar.future_end_limit
					RecurrenceEnd_time = gregorian_calendar.make_time(gregorian_calendar.future_end_limit)
				else:
					RecurrenceEnd = gregorian_calendar.gregorian_from_OLE_time(item_recurrence.PatternEndDate)
					RecurrenceEnd_time = gregorian_calendar.make_time(RecurrenceEnd)
				
				if low_cutoff_time  > RecurrenceEnd_time:
					continue

				# else better handle it.
			else:
				#print "info: ignoring \"%s\" (too old)" % (item.Subject)
				continue

		if item_start_time > cutoff_time:
			#if is_verbose:
			#print >>sys.stderr, "info: ignoring \"%s\" (too far in the future: \"%s\")." % (item.Subject, start)
			continue
		
		yield get_next_times_for_appointment(item, low_cutoff)
