<?xml version="1.1" ?>
<diagram author="Simon Van Mierlo+Raphael Mannadiar" name="Bouncing_Balls_Python_Version">
	<description>
		Tkinter frame with bouncing balls in it.
	</description>
	<top>
		from python_runtime.libs.ui import ui
		from python_runtime.libs.utils import utils
	</top>
	<inport name="ui"/>
	<class name="MainApp" default="true">
		<relationships>
			<association name="fields" class="Field" />
		</relationships>
		<method name="MainApp">
			<body>
				<![CDATA[
				self.nr_of_fields = 0
				]]>
			</body>
		</method>
		<scxml initial="running">
			<state id="running" initial="root">
				<parallel id="root">
					<state id="main_behaviour" initial="initializing">
						<state id="initializing">
							<transition target="../running">
								<raise event="create_field" />		
							</transition>
						</state>
						<state id="running">
							<transition target='.' event='button_pressed' cond='event_name == "create_new_field"'>
								<parameter name="event_name" type="str" />
								<raise event="create_field" />
							</transition>
						</state>
					</state>
					<state id="cd_behaviour" initial="waiting">
						<state id="waiting">
							<transition event="create_field" target="../creating">
								<raise scope="cd" event="create_instance">
									<parameter expr='"fields"' />
								</raise>
							</transition>
							<transition event="delete_field" target='../check_nr_of_fields'>
								<parameter name="association_name" type="str"/>
								<raise scope="cd" event="delete_instance">
									<parameter expr='association_name' />
								</raise>
								<script>
									<![CDATA[
									self.nr_of_fields -= 1
									]]>
								</script>
							</transition>
						</state>
						<state id="creating">
							<transition event="instance_created" target="../waiting">
								<parameter name="association_name" type="string"/>
								<raise scope="cd" event="start_instance">
									<parameter expr="association_name" />
								</raise>
								<raise scope="narrow" event="set_association_name" target="association_name">
									<parameter expr="association_name" />
								</raise>
								<script>
									<![CDATA[
									self.nr_of_fields += 1
									]]>
								</script>
							</transition>
						</state>
						<state id="check_nr_of_fields">
							<transition target="../../../stopped" cond="self.nr_of_fields == 0">
								<script>
									<![CDATA[
									ui.close_window(ui.window)
									]]>
								</script>
							</transition>
							<transition target="../waiting" cond="self.nr_of_fields != 0"/>
						</state>
					</state>
				</parallel>
				<state id="stopped" />
			</state>
		</scxml>
	</class>

	<class name="Field">
		<attribute name="canvas" />
		<attribute name="field_window" />
        <inport name="field_ui" />
		<relationships>
			<association name="balls" class="Ball" />
			<association name="buttons" class="Button" />
			<association name="parent" class="MainApp" min="1" max="1" />
		</relationships>
		<method name="Field">
			<body>
				<![CDATA[
				self.field_window = ui.new_window(400,450);
				self.canvas			= ui.append_canvas(self.field_window,400,400,{'background':'#eee'});
				ui.bind_event(self.field_window,		ui.EVENTS.WINDOW_CLOSE,			self.controller,	'window_close');
				ui.bind_event(self.field_window,		ui.EVENTS.KEY_PRESS, 			self.controller, 	'key_press');
				ui.bind_event(self.canvas.element,	ui.EVENTS.MOUSE_RIGHT_CLICK,	self.controller,	'right_click', self.inports["field_ui"]);
				ui.bind_event(self.canvas.element,	ui.EVENTS.MOUSE_MOVE, 			self.controller, 	'mouse_move');
				ui.bind_event(self.canvas.element,	ui.EVENTS.MOUSE_RELEASE, 		self.controller, 	'mouse_release');
				]]>
			</body>
		</method>
		<method name="~Field">
			<body>
				<![CDATA[
				ui.close_window(self.field_window);
				]]>
			</body>
		</method>
		<scxml initial="root">
			<state id="root" initial="waiting">
				<state id="waiting">
					<transition event="set_association_name" target="../initializing">
						<parameter name="association_name" type="str" />
						<script>
							<![CDATA[
							self.association_name = association_name
							]]>
						</script>
					</transition>
				</state>
				<state id="initializing">
					<transition target="../creating">
						<raise scope="cd" event="create_instance">
							<parameter expr='"buttons"' />
							<parameter expr='"Button"' />
							<parameter expr="self" />
							<parameter expr="'create_new_field'" />
							<parameter expr="'Spawn New Window'" />
						</raise>
					</transition>
				</state>
				<state id="creating">
					<transition event='instance_created' target='../packing'>
						<parameter name="association_name" type="string"/>
						<raise scope="cd" event="start_instance">
							<parameter expr="association_name" />
						</raise>
					</transition>
				</state>
				<state id="packing">
					<transition event="button_created" target='../running'>
					</transition>
				</state>
				<parallel id="running">
					<transition	port="ui" event="window_close" target="../deleting" cond="window == self.field_window or window == ui.window">
						<parameter name="window" />
						<raise scope="narrow" target="'balls'" event='delete_self' />
						<raise event="delete_instance" scope="cd">
							<parameter expr='"buttons"' />
						</raise>
					</transition>
					<state id="main_behaviour" initial="running">
						<state id="running">
							<transition port="field_ui" event="right_click" target="../creating">
								<parameter name="x" />
								<parameter name="y" />
								<parameter name="button" />
								<raise scope="cd" event="create_instance">
									<parameter expr='"balls"' />
									<parameter expr='"Ball"' />
									<parameter expr="self.canvas" />
									<parameter expr="x" />
									<parameter expr="y" />
									<parameter expr="self.field_window" />
								</raise>
							</transition>
						</state>
						<state id="creating">
							<transition event="instance_created" target="../running">
								<parameter name="association_name" type="string"/>
								<raise scope="cd" event="start_instance">
									<parameter expr="association_name" />
								</raise>
								<raise scope="narrow" event="set_association_name" target="association_name">
									<parameter expr="association_name" />
								</raise>
							</transition>
						</state>
					</state>
					<state id="deleting_behaviour" initial="running">
						<state id="running">
							<transition event="delete_ball" target='.'>
								<parameter name="association_name" type="str"/>
								<raise scope="cd" event="delete_instance">
									<parameter expr='association_name' />
								</raise>
							</transition>
						</state>
					</state>
					<state id="child_behaviour" initial="listening">
						<state id="listening">
							<transition event="button_pressed" target='.'>
								<parameter name="event_name" type="str" />
								<raise event="button_pressed" scope="narrow" target="'parent'">
									<parameter expr='event_name' />
								</raise>
							</transition>
						</state>
					</state>
				</parallel>
				<state id="deleting">
					<transition after="0.05" target="../deleted">
						<raise event="delete_field" scope="narrow" target="'parent'">
							<parameter expr='self.association_name' />
						</raise>
					</transition>
				</state>
				<state id="deleted" />
			</state>
		</scxml>
	</class>
	
	<class name="Button">
		<relationships>
			<association name="parent" class="Field" min="1" max="1" />
		</relationships>
		<method name="Button">
			<parameter name="parent" type="Field" />
			<parameter name="event_name" type="str" />
			<parameter name="button_text" type="str" />
			<body>
				<![CDATA[
				self.event_name = event_name;
				button = ui.append_button(parent.field_window, event_name);
				ui.bind_event(button.element, ui.EVENTS.MOUSE_CLICK, self.controller, 'mouse_click');
				]]>
			</body>
		</method>
		<scxml initial="initializing">
			<state id="initializing">
				<transition target="../running">
					<raise event="button_created" scope="narrow" target="'parent'">
					</raise>
				</transition>
			</state>
			<state id="running">
				<transition port='ui' event="mouse_click" target='.' cond="button == ui.MOUSE_BUTTONS.LEFT">
					<parameter name="x" />
					<parameter name="y" />
					<parameter name="button" />
					<raise event="button_pressed" scope="narrow" target="'parent'">
						<parameter expr="self.event_name" />
					</raise>
				</transition>
			</state>
		</scxml>
	</class>
	
	<class name="Ball">
		<atrribute name="element" />
		<attribute name="canvas" />
		<attribute name="field_window" />
		<inport name="ball_ui" />
		<relationships>
			<association name="parent" class="Field" min="1" max="1" />
		</relationships>
		<method name="Ball">
			<parameter name="canvas" />
			<parameter name="x" />
			<parameter name="y" />
			<parameter name="field_window" />
			<body>
				<![CDATA[
				self.canvas 		= canvas;
				self.field_window	= field_window;
				self.r 				= 20.0;
				self.vel 			= {'x': utils.random()*2.0-1.0, 'y': utils.random()*2.0-1.0};
				self.mouse_pos		= {};
				self.smooth 		= 0.4; # value between 0 and 1

				circle = self.canvas.add_circle(x, y, self.r, {'fill':'#000'});
				ui.bind_event(circle, ui.EVENTS.MOUSE_PRESS, 		self.controller, 'mouse_press',self.inports["ball_ui"]);
				ui.bind_event(circle, ui.EVENTS.MOUSE_RIGHT_CLICK,	self.controller, 'right_click');
				self.element = circle;
				]]>
			</body>
		</method>
		<method name="~Ball">
			<body>
				<![CDATA[
				self.canvas.remove_element(self.element);
				]]>
			</body>
		</method>
		<scxml initial="main_behaviour">
			<state id="main_behaviour" initial="initializing">
				<state id="initializing">
					<transition event="set_association_name" target="../bouncing">
						<parameter name="association_name" type="str" />
						<script>
							<![CDATA[
							self.association_name = association_name
							]]>							
						</script>
					</transition>
				</state>
				<state id="bouncing">
					<transition after="0.01" target=".">
						<script>
							<![CDATA[
							pos = self.element.get_position();	
							if pos.x-self.r <= 0 or pos.x+self.r >= self.canvas.width :
								self.vel['x'] = -self.vel['x'];
							if pos.y-self.r <= 0 or pos.y+self.r >= self.canvas.height :
								self.vel['y'] = -self.vel['y'];
							self.element.move(self.vel['x'], self.vel['y']);
							]]>							
						</script>
					</transition>
					<transition port="ball_ui" event="mouse_press" target="../selected" cond="button == ui.MOUSE_BUTTONS.LEFT">
						<parameter name="x" />
						<parameter name="y" />
						<parameter name="button" />
						<script>
							<![CDATA[
							self.element.set_color("#ff0");
							]]>							
						</script>
					</transition>
				</state>
				<state id="dragging">
					<transition port="ui" event="mouse_move" target=".">
						<parameter name="x" />
						<parameter name="y" />
						<parameter name="button" />
						<script>
							<![CDATA[
							dx = x - self.mouse_pos['x'];
							dy = y - self.mouse_pos['y'];

							self.element.move(dx, dy);

							# keep ball within boundaries
							pos = self.element.get_position();
							if pos.x-self.r <= 0 :
								pos.x = self.r + 1;
							elif pos.x+self.r >= self.canvas.width :
								pos.x = self.canvas.width-self.r-1;
							if pos.y-self.r <= 0 :
								pos.y = self.r + 1;
							elif pos.y+self.r >= self.canvas.height :
								pos.y = self.canvas.height-self.r-1;
							self.element.set_position(pos.x, pos.y);
							self.mouse_pos = {'x':x, 'y':y};
							self.vel = {
								'x': (1-self.smooth)*dx + self.smooth*self.vel['x'],
								'y': (1-self.smooth)*dy + self.smooth*self.vel['y']
							};
							]]>
						</script>
					</transition>
					<transition port="ui" event="mouse_release" target="../bouncing">
						<parameter name="x" />
						<parameter name="y" />
						<script>
							<![CDATA[
							self.element.set_color("#f00");
							]]>							
						</script>
					</transition>
				</state>
				<state id='selected'>
					<transition port="ball_ui" event="mouse_press" target="../dragging" cond="button == ui.MOUSE_BUTTONS.LEFT">
						<parameter name="x" />
						<parameter name="y" />
						<parameter name="button" />
						<script>
							<![CDATA[
							self.mouse_pos = {'x':x, 'y':y};
							]]>
						</script>
					</transition>
					<transition port="ui" event="key_press" target="." cond="key == ui.KEYCODES.DELETE and active_window == self.field_window">
						<parameter name="key" />
						<parameter name="active_window" />
						<raise event="delete_self" scope="local" />
					</transition>
				</state>
				<transition event="delete_self" target='../deleted'>					
					<raise event="delete_ball" scope="narrow" target="'parent'">
						<parameter expr='self.association_name' />
					</raise>
				</transition>
			</state>
			<state id='deleted' />
		</scxml>
	</class>
</diagram>

