#from animsetup import *
# imports everything from animsetup, including global variables such as width,
# height, delaytime.
#!/usr/bin/env python
# Animation Template.  User must define a mydraw(brush,gc)

import pygtk
import gtk
import gobject
import time
import threading
from random import *

width,height = 900,700
delaytime = 80   # 100ms delay between animation frames

window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.set_title("Simplified Drawing Example")
window.connect("destroy", lambda w: gtk.main_quit())
area = gtk.DrawingArea()
area.set_size_request(width,height)
window.resize(width,height)
window.add(area)

window.show_all()
window.move(0,0)

gc0 = area.window.new_gc()
cmap = gc0.get_colormap()

# common colors
green = cmap.alloc_color(red=0,green=65535,blue=0)
blue = cmap.alloc_color(red=0,green=0,blue=65535)
red = cmap.alloc_color(red=65535,green=0,blue=0)
white = cmap.alloc_color("white")
black = cmap.alloc_color("black")
gray = cmap.alloc_color("gray")
yellow = cmap.alloc_color("yellow")
purple = cmap.alloc_color("purple")

gc0.set_foreground(green)
gc0.set_background(blue)
gc0.line_width = 1

# double buffering setup
dbuf = gtk.gdk.Pixmap(area.window,width,height,-1)
gc1 = dbuf.new_gc()
gc1.set_colormap(cmap)

### Thread control
syn = threading.Event()   # throttles mainloop
syn2 = threading.Event()  # throttles user thread
syn.clear()
syn2.clear()
stopall = False           # for cleanup


class MyThread ( threading.Thread ):
   def __init__(self,brush,gc):
       self.win = brush
       self.dgc = gc
       threading.Thread.__init__(self)
   def run ( self ):
       global mydraw
       mydraw(self.win,self.dgc)   

eehandler = 0
def area_expose_cb(area, event):
    global mainloop,eehandler
    my = MyThread(dbuf,gc1)
    my.start()             # start student thread, which calls mydraw
    area.handler_block(eehandler)  # prevent restart when window moves.
    mainloop(area.window)  # start main animation refresh cycle
    return True

# get ready for expose event
eehandler = area.connect("expose_event", area_expose_cb)


# animation refresh control loop:
def mainloop(brush):
    if not stopall:
        syn.wait()  # wait for permission to refresh screen
        syn.clear() # reset for next round
        brush.draw_drawable(gc0,dbuf,0,0,0,0,width,height) # refresh
        syn2.set()   # informs user thread to goto next iteration
        eid = gobject.timeout_add(delaytime, mainloop, brush) # reschedule
        return False
# end mainloop

def updateDisplay():
    syn.set()         # release mainloop to refresh screen
    syn2.wait()       # synch with mainloop before looping again
    syn2.clear()      # reset for next round
# end updateDisplay

def cleanup():
    global stopall
    stopall = True
    syn.set()
    syn2.set()
# end cleanup

# default mydraw function will be overriden
#def mydraw(brush,gc):
#    print "default mydraw - should redefine your own"
# default mydraw



##########################################################################

def drawcircle(brush,gc,x,y,radius,fill):
    brush.draw_arc(gc,fill,x-radius,y-radius,2*radius,2*radius,0,360*64)

# distance:
def dist(x1,y1,x2,y2):
   dx = x1-x2
   dy = y1-y2
   return (dx*dx + dy*dy)**0.5

# make sure coordinates stay within width, height:
def inbounds(x,y,rad):
    if (x<rad): x = width-rad
    if (x>(width-rad)): x = rad
    if (y<rad): y = height-rad
    if (y>height-rad): y = rad
    return (x,y)     # return the tuple x,y
# end inbounds


### class for a stick figure
class stickfigure:

   def __init__(self,x,y,scale,brush,gc):
      self.x = x
      self.y = y
      self.scale = scale
      self.color = blue   # default color
      self.armfactor = 0  # straight arms
      self.mouthfactor = 1  # smile
      self.brush = brush   # drawing window
      self.gc = gc         # drawing graphical context
      self.calcpoints()    # call procedure to set coordinates
      self.observers = []  # Be observee
   # __init__

   def calcpoints(self):
      x,y,scale = self.x,self.y,self.scale   # for convenience
      self.chest = (x,y)
      self.stomach = (x,y+scale) 
      self.neck = (x,y-(scale/2)) 
      self.nose = (x,self.neck[1]-(scale/2)) 
      self.lleg = (x-scale,y+(2*scale)) 
      self.rleg = (x+scale,y+(2*scale)) 
      self.leye = (x-(scale/4),self.neck[1]-((3*scale)/4)) 
      self.reye = (x+(scale/4),self.neck[1]-((3*scale)/4)) 
      self.lmouth = (x-(scale/4),self.neck[1]-(scale/4)) 
      self.rmouth = (x+(scale/4),self.neck[1]-(scale/4)) 
      self.larm = (x-scale,y+(self.armfactor*scale/2)) 
      self.rarm = (x+scale,y+(self.armfactor*scale/2)) 
      self.lip = (x,self.neck[1]-(scale/4)+(self.mouthfactor*scale/8))
   # calcpoints

   def draw(self):
      x,y,scale = self.x,self.y,self.scale   # for convenience
      gc,brush = self.gc, self.brush
      leye,reye,lip = self.leye,self.reye,self.lip
      lmouth,rmouth,nose = self.lmouth,self.rmouth,self.nose
      neck,chest,stomach = self.neck,self.chest,self.stomach
      larm,rarm,lleg,rleg = self.larm,self.rarm,self.lleg,self.rleg
      gc.set_foreground(self.color)
      drawcircle(brush,gc,leye[0]-3,leye[1]-3,3,True)    # left eye
      drawcircle(brush,gc,reye[0]-3,reye[1]-3,3,True)    # right eye
      brush.draw_line(gc,lmouth[0],lmouth[1],lip[0],lip[1])  # mouth and lip
      brush.draw_line(gc,lip[0],lip[1],rmouth[0],rmouth[1]) 
      drawcircle(brush,gc,nose[0],nose[1],3,False)            # nose
      drawcircle(brush,gc,nose[0],nose[1],(scale/2),False)    # new head
      brush.draw_line(gc,neck[0],neck[1],stomach[0],stomach[1])    # body
      brush.draw_line(gc,chest[0],chest[1],larm[0],larm[1])        # left arm
      brush.draw_line(gc,chest[0],chest[1],rarm[0],rarm[1])        # right arm
      brush.draw_line(gc,stomach[0],stomach[1],lleg[0],lleg[1])    # left leg
      brush.draw_line(gc,stomach[0],stomach[1],rleg[0],rleg[1])    # right leg
   # draw

   ## attribute methods:

   def armsup(self):
      self.armfactor = -1
      self.calcpoints()  # recalculate all coordinates

   def armsdown(self):
      self.armfactor = 1
      self.calcpoints()

   def armsout(self):
      self.armfactor = 0
      self.calcpoints()

   def besad(self):
      self.mouthfactor = -1  # lip is above mouth corners
      self.calcpoints()

   def move(self,dx,dy):
      self.x += dx
      self.y += dy
      self.x,self.y = inbounds(self.x,self.y,self.scale/2)
      self.calcpoints()
      self.notify()

   def setcolor(self,c):
      self.color = c


######## Observee Interface:

   def attach(self,obvr):
      self.observers.append(obvr)

   def detach(self,obvr):
      self.observers.remove(obvr)

   def notify(self):
      for x in self.observers:
         x.update(self)
   #notify

# end class stickfigure

############ The Observer class is now a subclass of stickfigure

class chasefig(stickfigure):
   def __init__(self,x,y,scale,brush,gc):
      stickfigure.__init__(self,x,y,scale,brush,gc) # call superclass cons.
   # init

   ## Observer interface:

   def update(self,observee):  # synch with obsevee, chases observee
      dx, dy = 4,4             # determine movement vector
      if observee.x < self.x: dx = -4  # go left
      if observee.y < self.y: dy = -4  # go up
      self.move(dx,dy)         # chase after observee
   #update

# class chasefig


def mydraw(brush,gc):   # this function is called automatically.
   i = 0
   fig1 = stickfigure(width-164,height/2,64,brush,gc)
   fig2 = chasefig(50,50,48,brush,gc)
   fig2.setcolor(red)
   fig1.attach(fig2)   # attach fig2 to fig1
   fig1.draw()
   fig2.draw()
   cx,cy = randint(-5,5),randint(-5,5) # fig1 movement vector
   while i<1000:
        gc.set_foreground(white) #clear background
        brush.draw_rectangle(gc,True,0,0,width,height)
        if (i%3) == 0: fig1.armsup()
        if (i%3) == 1: fig1.armsout()
        if (i%3) == 2: fig1.armsdown()
        fig1.move(cx,cy)
        fig1.draw()
        fig2.draw()  # this is only call to fig2, which is an observer
        i+=1         # its moves are controled autonomously
        updateDisplay()
   # end while
   cleanup()   # should be called after end of all animation
# end mydraw

##########################################################################
# The following line must be the last line in the program
gtk.main()


