import sys, random, string
from Heap import Heap

length = 100         # length of plank in meters
speed = 1            # pirate walking speed in meters per second
npirates = 10         # number of pirates
gtime = 0             # global clock

def ids():
    for c in string.uppercase:
        yield c

idlist = ids()

class Pirate:
    """a pirate has a position, direction and speed
    """
    def __init__(self):
        self.id = idlist.next()
        self.position = random.uniform(0, length)
        self.direction = random.choice([-1, 1])
        self.speed = speed

    def move(self, dt):
        """move the pirate the distance he moves in time dt"""
        self.position += self.direction * self.speed * dt

    def predict_fall(self):
        """predict when this pirate will fall off the plank (assuming
        no collisions) and return an event object
        """
        if self.direction == -1:
            d = self.position
        else:
            d = length - self.position
        dt = d / self.speed
        e = make_fall(gtime+dt, self)
        return e

    def __str__(self):
        return 'Pirate %s at position %.4g' % (self.id,
                                               self.position * self.direction)

# list of pirates
pirates = [Pirate() for p in range(npirates)]

# event queue
heap = Heap()

class Event(list):
    """An Event object is a list with one element, the time
    at which the event occurs.  Event objects also have an
    attribute named handle which is a reference to the method
    that will be used to handle the event.
    """
    def __init__(self, time):
        self.append(time)

    # time is stored as the first element of the list, but
    # can be accessed as if it were an instance variable.
    def get_time(self): return self[0]
    def set_time(self, t): self[0] = t
    time = property(get_time, set_time)

    # the following methods are event handlers
    
    def handle_fall(self):
        p = self.pirate
        print p, 'falls at time', gtime
        pirates.remove(p)


def make_fall(time, pirate):
    """return a new fall Event with the given time and pirate
    """
    e = Event(time)
    e.pirate = pirate
    e.handle = e.handle_fall
    return e


def main(name, *args):
    global gtime

    # initialize the heap with one event per pirate
    for p in pirates:
        print p
        e = p.predict_fall()
        heap.push(e)

    # main loop
    while heap:

        # get the next event
        e = heap.popmin()

        # move all the pirates
        dt = e.time - gtime        
        for p in pirates:
            p.move(dt)

        # update the global clock and handle the event
        gtime = e.time
        e.handle()
    
    print 'Last pirate fell at time', gtime
    print pirates

if __name__ == '__main__':
    main(*sys.argv)
