
"""Event queues - used to schedule delayed events.

Possible classes you can extend:
RealTimeManager - Shows which routines a time manager should define
EventQueue - if you need more complex event timing logic """

import time, bisect

class RealTimeManager:

    """ RealTimeManager - handles times of real-world seconds """
    
    def curtime( s ):
        return time.time()

    def conv_time( s, time ):
        return time + s.curtime()

    def comp_time( s, time ):
        return max( 0, time - s.curtime() )

class VirtualTimeManager( RealTimeManager ):

    """ VirtualTimeManager - handles times of explicit tick()s """

    def __init__( s, time = 0 ):
        s.settime( time )

    def tick( s ):
        s.time = s.time + 1

    def settime( s, time ):
        s.time = time

    def curtime( s ):
        return s.time

    def comp_time( s, time ):
        if time <= s.curtime(): return 0
        return None

class EventQueue:

    """ EventQueue - basic event queue

    The time manager is called to manage event times. The only way
    EventQueue itself uses times is to keep its internal queue in
    sorted order. Therefore, non-numeric times should define __cmp__(). """

    def __init__( s, timemgr = RealTimeManager() ):

        """ Initialise variables for the event queue """
        
        s.clear()
        s.timemgr = timemgr

    def add_event( s, event, time ):

        """ Schedule event for later activating.

        time is relational to the current time. """
        
        time = s.timemgr.conv_time( time )
        bisect.insort( s.events, ( time, event ))

    def cancel_event( s, event ):

        """ Cancel every instance of already-scheduled event.

        This method cancels all, not just the first one, instances of
        the event, even if it is a persistent event. """
        
        s.events = filter( lambda x: x[1] != event, s.events )

    def add_persistent( s, event, time ):

        """ Add persistent event.

        Persistent events are reinstated again and again. time is the
        interval of reinstating. """
        
        s.persistent[event] = time
        s.add_event( event, time )

    def del_persistent( s, event ):

        """ Remove persistent event. This does not cancel the event. """
        
        if s.persistent.has_key( event ): del s.persistent[event]

    def empty( s ):
        return len( s.events ) == 0

    def next_event_delay( s ):
        if s.events: return s.timemgr.comp_time( s.events[0][0] )
        return None

    def process( s ):
        while s.next_event_delay() == 0:
            event = s.events[0][1]
            del s.events[0]
            event()
            if s.persistent.has_key( event ):
                s.add_event( event, s.persistent[event] )

    def clear( s ):
        s.events = []
        s.persistent = {}

