# -*- Mode: Python; tab-width: 4 -*-

# This is an alternative event loop that supports 'schedulable events'.
# You can specify an event callback to take place after <n> seconds.

# Important usage note: The granularity of the time-check is limited
# by the <timeout> argument to 'go()'; if there is little or no
# activity and you specify a 30-second timeout interval, then the
# schedule of events may only be checked at those 30-second intervals.
# In other words, if you need 1-second resolution, you will have to
# poll at 1-second intervals.  This facility is more useful for longer
# timeouts ("if the channel doesn't close in 5 minutes, then forcibly
# close it" would be a typical usage).

import asyncore
import time

socket_map = asyncore.socket_map

class event_loop:

	def __init__ (self):
		self.events = {}

	def go (self, timeout=30.0):
		events = self.events
		while socket_map:
			now = int(time.time())
			for k,v in events.items():
				if now >= k:
					v (self, now)
					del events[k]
			asyncore.poll (timeout)
			
	def schedule (self, delta, callback):
		now = int (time.time())
		self.events[now + delta] = callback

	def unschedule (self, callback, all=1):
		"unschedule a callback"
		for k,v in self.events:
			if v is callback:
				del self.events[k]
				if not all:
					break

class test (asyncore.dispatcher):
	
	def __init__ (self):
		asyncore.dispatcher.__init__ (self)

	def handle_connect (self):
		print 'Connected!'

	def writable (self):
		return not self.connected

	def connect_timeout_callback (self, event_loop, when):
		if not self.connected:
			print 'Timeout on connect'
			self.close()

	def periodic_thing_callback (self, event_loop, when):
		print 'A Periodic Event has Occurred!'
		# re-schedule it.
		event_loop.schedule (15, self.periodic_thing_callback)
		
if __name__ == '__main__':
	import socket
	el = event_loop()
	t = test ()
	t.create_socket (socket.AF_INET, socket.SOCK_STREAM)
	el.schedule (10, t.connect_timeout_callback)
	el.schedule (15, t.periodic_thing_callback)
	t.connect (('207.30.71.133', 80))
	el.go(1.0)
