#!/usr/bin/env python
import cairo
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GLib, GObject
import threading
import time
import _thread
from random import random,randint
from math import pi

width,height = 1024,768
win = Gtk.Window()
drawingarea = Gtk.DrawingArea()
win.add(drawingarea)
brush = 1
da = 1
delaytime = 500
eehandler = 0

### Thread control
syn = threading.Event()   # throttles mainloop
syn2 = threading.Event()  # throttles user thread
syn.clear()  # .set allows to go through, .clear blocks
syn2.clear()  # block
stopall = False           # for cleanup

# old code, Thread permits a while loop inside mydraw
class MyThread ( threading.Thread ):
    def __init__(self,d,b):
        global brush,da
        threading.Thread.__init__(self)
        self.da = d
        self.br = b
        brush = b
        da = d
    def run ( self ):
        global mydraw
        mydraw()
#class MyThread

# animation refresh control loop:
def mainloop(da1, brush1):
    global da,brush
#    da = da1
    brush = brush1
#    print("mainloop")	
    if not stopall:
        syn.wait()  # wait for permission to refresh screen
        syn.clear() # reset for next round
        brush.stroke() 
        syn2.set()   # informs user thread to goto next iteration
        win.queue_draw_area(0,0,width-1,height-1)
        return False
# end mainloop

def area_expose_cb(daa, bbrush):	
    global brush, da, eehandler,mainloop
    brush = bbrush
    my = MyThread(daa,bbrush)
    my.start()
    drawingarea.handler_block(eehandler)
    eeh = drawingarea.connect('draw', mainloop)  # reconnect
    #win.queue_draw()
    mainloop(daa,bbrush)  # sxxtart main animation refresh cycle
    return True

win.connect('destroy', lambda w: Gtk.main_quit())
win.set_default_size(width,height)
eehandler = drawingarea.connect('draw', area_expose_cb)
win.show_all()


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

### some common colors as RGB triples:
black = (0,0,0)
white=(1,1,1)
red = (1,0,0)
green = (0,1,0)
blue=(0,0,1)
yellow = (1,1,0)
aqua=(0,1,1)
magenta=(1,0,1)
gray=(.5,.5,.5)
purple=(.5,0,0.5)

COLORS = [black,white,red,green,blue,yellow,aqua,magenta,gray,purple]

##########################################################################
############################# STUDENT CODE ###############################
##########################################################################



## To affect animation, entire image must be redrawn with each updateDisplay
## Animate one moving circle, defined by x,y,r,dx,dy,c for color

def mydraw():
    # sets attributes of circles
    CLS = [white,red,green,blue,yellow,aqua,magenta,gray,purple]
    n = 200  # number of circles
    R = []  # array of radii, replaces  r = randint(8,64)
    i=0
    while i<n:
        R.append(randint(8,64))
        i += 1
    # constructs R array

    #x,y = width//2, height//2   # start at middle
    #dx,dy = randint(-10,10), randint(-6,6)
    #color = CLS[ randint(1,len(CLS)-1) ]
    
    X = [width//2]*n  # pre-construct array of 200 0's
    Y = [height//2]*n  # Y array
    DX = [0]*n
    DY = [0]*n
    COLOR = [red]*n
    i = 0
    while i<n:
        DX[i] = randint(-10,10)
        DY[i] = randint(-8,8)
        COLOR[i] = CLS[ randint(1,len(CLS)-1) ]
        i+=1
    #construct other attributes of circles
         
    while True:
        brush.set_source_rgb(*black)  # * needed to unravel tuple
        brush.paint()  # paints over background to draw new frame
        # draw circle - now need nested loop:
        i = 0
        while i<n:
            brush.set_source_rgb(*COLOR[i])
            brush.new_sub_path()    # avoids extra line in arc
            brush.arc(X[i],Y[i],R[i],0,2*pi)   # circle
            brush.fill()  # solid circle

            X[i] += DX[i]  # change position by movement vector
            Y[i] += DY[i]
            if (X[i]<=R[i] or X[i]>=width-R[i]): DX[i] *= -1
            if (Y[i]<=R[i] or Y[i]>=height-R[i]): DY[i] *= -1

            i+=1
        # while i<n
        
        updateDisplay()   # draw frame
        time.sleep(0.1)  # animation delay
    return False
#mydraw




#### The following must be the last line of the program: don't move
Gtk.main()
