Diagram(name = 'BouncingBalls',
		author = 'Simon Van Mierlo',
		description = 'Tkinter frame with bouncing balls in it.'):
	Top {
		import time
		import random
		import Tkinter as tk
		from mvk_widget import MvKWidget
	}
	Inport(name = 'input')

	Class(name = 'MainApp', default = True):
		Association(name = 'fields', class = 'Field')
		#Inheritance(class = 'RuntimeClassBase', priority = 1)
		Inheritance(class = 'tk.Tk', priority = 0)

		Constructor {
			tk.Tk->__init__(self)
			self.fixed_update_time = 20
			self->update_self()
			self->withdraw()
			self.nr_of_fields = 0
		}
		Method update_self() {
			self.controller->update(self.fixed_update_time / 1000.0)
			self.schedule_time = time->time()
			self.scheduled_update_id = self->after(self.fixed_update_time, self.update_self)
		}
		StateMachine:
			initial = 'running'
			State('running'):
				initial = 'root'
				Orthogonal('root'):
					State('main_behaviour'):
						initial = 'initializing'
						State('initializing'):
							Transition(target='../running'):
								raise += { create_field() }
						State('running'):
							Transition(target='.'):
								event = button_pressed(event_name: String)
								guard = {event_name == 'create_new_field'}
								raise += { create_field() }
					State('cd_behaviour'):
						initial = 'waiting'
						State('waiting'):
							Transition(target='../creating'):
								event = create_field()
								raise += { scope=cd, create_field('fields') }
							Transition(target='../check_nr_of_fields'):
								event = delete_field(association_name: String)
								raise += { scope=cd, delete_instance(association_name) }
								Actions {
									self.nr_of_fields -= 1
								}
						State('creating'):
							Transition(target='../waiting'):
								event = instance_created(association_name: String)
								raise += { scope=cd, start_instance(association_name) }
								raise += { scope=narrow, target=association_name, set_association_name(association_name) }
								Actions {
									self.nr_of_fields += 1
								}
						State('check_nr_of_fields'):
							Transition(target='../../../stopped'):
								guard = {self.nr_of_fields == 0}
								Actions {
									self->destroy()
								}
							Transition(target='../waiting'):
								guard = {self.nr_of_fields != 0}
				State('stopped')


	Class(name = 'Field'):
		Association(name = 'balls', class = 'Ball')
		Association(name = 'buttons', class = 'Button')
		Association(name = 'parent', class = 'MainApp', min = 1, max = 1)
		#Inheritance(class = 'RuntimeClassBase', priority = 1)
		Inheritance(class = 'tk.Toplevel', priority = 0)
		Inheritance(class = 'MvKWidget', priority = -1)

		Constructor {
			tk.Toplevel->__init__(self)
			self->title('BouncingBalls')

			CANVAS_SIZE_TUPLE = (0, 0, self->winfo_screenwidth() * 2, self->winfo_screenheight() * 2)
			self.c = tk->Canvas(self, relief=tk.RIDGE, scrollregion=CANVAS_SIZE_TUPLE)

			MvKWidget->__init__(self, self.controller, self.c)
		}
		Destructor {
			self->destroy()
		}
		StateMachine:
			initial = 'root'
			State('root'):
				initial = 'waiting'
				State('waiting'):
					Transition(target='../initializing'):
						event = set_association_name(association_name: String)
						Actions {
							self.association_name = association_name
						}
				State('initializing'):
					Transition(target='../creating'):
						raise += { scope=cd, create_instance('buttons', self, 'create_new_field', 'Spawn New Window') }
				State('creating'):
					Transition(target='../packing'):
						event = instance_created(association_name: String)
						raise += { scope=cd, start_instance(association_name) }
				State('packing'):
					Transition(target='../running'):
						event = button_created(button: Button)
						Actions {
							button->pack(expand=False, fill=tk.X, side=tk.TOP)
							self.c->focus_force()
							self.c->pack(expand=True, fill=tk.BOTH)
						}
				Orthogonal('running'):
					Transition(port = 'input', target='../deleting'):
						event = window-close(tagorid: Integer='None')
						raise += { scope=narrow, target='balls',delete_self() }
					State('main_behaviour'):
						initial = 'running'
						State('running'):
							Transition(port = 'input', target='../creating'):
								event = right-click(tagorid: Integer='None')
								raise += { scope=cd, create_instance('balls', self.c, self.last_x, self.last_y) }
						State('creating'):
							Transition(target='../running'):
								event = instance_created(association_name: String)
								raise += { scope=cd, start_instance(association_name) }
								raise += { scope=narrow, target=association_name, set_association_name(association_name) }
					State('deleting_behaviour'):
						initial = 'running'
						State('running'):
							Transition(target='.'):
								event = delete_ball(association_name: String)
								raise += { scope=cd, delete_instance(association_name) }
					State('child_behaviour'):
						initial = 'listening'
						State('listening'):
							Transition(target='.'):
								event = button_pressed(event_name: String)
								raise += { scope=narrow, target='parent', button_pressed(event_name) }
				State('deleting'):
					Transition(after=0.05, target='../deleted'):
						raise += { scope=narrow, target='parent', delete_field(self.association_name) }
				State('deleted')
	Class(name = 'Button'):
		Association(name = 'parent', class = 'Field', min = 1, max = 1)
		#Inheritance(class = 'RuntimeClassBase', priority = 1)
		Inheritance(class = 'MvKWidget', priority = 0)
		Inheritance(class = 'tk.Button', priority = -1)

		Constructor(parent: Field, event_name: String, button_text: String) {
				tk.Button->__init__(self, parent, text=button_text)
				MvKWidget->__init__(self, self.controller)
				self.event_name = event_name
		}
		StateMachine:
			initial = 'initializing'
			State('initializing'):
				Transition(target='../running'):
					raise += { scope=narrow, target='parent', button_created(self) }
			State('running'):
				Transition(port = 'input', target='.'):
					event = left-click(tagorid: Integer='None')
					guard = {tagorid == id(self)}
					raise += { scope=narrow, target='parent', button_pressed(self.event_name) }
	Class(name = 'Ball'):
		Association(name = 'parent', class = 'Field', min = 1, max = 1)
		#Inheritance(class = 'RuntimeClassBase', priority = 1)
		Inheritance(class = 'MvKWidget', priority = 0)
		Attribute(name = 'canvas')

		Constructor(canvas, x, y) {
			self.canvas = canvas
			self.r = 15.0
			self.smooth = 0.4 # value between 0 and 1
			self.vel = ['x': random->random() * 2.0 - 1.0, 'y': random->random() * 2.0 - 1.0]
			self.id = self.canvas->create_oval(x, y, x + (self.r * 2), y + (self.r * 2), fill="black")
			MvKWidget->__init__(self, self.controller, self.canvas, self.id)
		}
		Destructor {
			self.canvas->delete(self.id)
		}
		StateMachine:
			initial = 'main_behaviour'
			State('main_behaviour'):
				initial = 'initializing'
				State('initializing'):
					Transition(target='../bouncing'):
						event = set_association_name(association_name: String)
						Actions {
							self.association_name = association_name
						}
				State('bouncing'):
					Transition(after=0.01, target='.'):
						Actions {
							pos = self.canvas->coords(self.id)
							x = self.canvas->canvasx(pos[0])
							y = self.canvas->canvasy(pos[1])
							if x <= 0 or x + (self.r * 2) >= self.canvas->canvasx(self.canvas->winfo_width()):
								self.vel['x'] = -self.vel['x']
							end
							if y <= 0 or y + (self.r * 2) >= self.canvas->canvasy(self.canvas->winfo_height()):
								self.vel['y'] = -self.vel['y']
							end
							self.canvas->move(self.id, self.vel['x'], self.vel['y'])
						}
					Transition(port = 'input', target='../selected'):
						event = left-click(tagorid: Integer='None')
						guard = {tagorid == id(self)}
						Actions {
							self.canvas->itemconfig(self.id, fill="yellow")
						}
				State('dragging'):
					Transition(port = 'input', target='.'):
						event = motion(tagorid: Integer='None')
						Actions {
							coords = self.canvas->coords(self.id)
							dx = self.canvas->canvasx(self.last_x) - self.canvas->canvasx(coords[0])
							dy = self.canvas->canvasx(self.last_y) - self.canvas->canvasy(coords[1])

							self.canvas->move(self.id, dx, dy)

							coords = self.canvas->coords(self.id) # keep ball within boundaries
							x = self.canvas->canvasx(coords[0])
							y = self.canvas->canvasy(coords[1])
							if x - self.r <= 0:
								x = 1
							else:
								if x + self.r >= self.canvas->winfo_width():
									x = self.canvas->winfo_width() - (2 * self.r) - 1
								end
							end
							if y - self.r <= 0:
								y = 1
							else:
								if y + self.r >= self.canvas->winfo_height():
									y = self.canvas->winfo_height() - (2 * self.r) - 1
								end
							end
							self.canvas->coords(self.id, x, y, x + (self.r * 2), y + (self.r * 2))
							self.vel = [
								'x': (1 - self.smooth) * dx + self.smooth * self.vel['x'],
								'y': (1 - self.smooth) * dy + self.smooth * self.vel['y']
							]
						}

					Transition(port = 'input', target='../bouncing'):
						event = left-release(tagorid: Integer='None')
						Actions {
							self.canvas->itemconfig(self.id, fill="red")
						}
				State('selected'):
					Transition(port = 'input', target='../dragging'):
						event = left-click(tagorid: Integer='None')
						guard = {tagorid == id(self)}
					Transition(port = 'input', target='.'):
						event = delete(tagorid: Integer='None')
						raise += { scope=local, delete_self() }
				Transition(target='../deleted'):
					event = delete_self(tagorid: Integer='None')
					raise += { scope=narrow, target='parent', delete_ball(self.association_name) }
			State('deleted')


