<?xml version="1.1" ?>
<diagram author="Joeri Exelmans+Raphael Mannadiar+Simon Van Mierlo" name="Bouncing_Balls_JavaScript_Version">
	<description>
		SVG canvas with bouncing balls in it.
	</description>
	<top>
	</top>	
	<inport name="ui" />
	<class name="MainApp" default="true">
		<relationships>
			<association name="fields" class="Field" />
		</relationships>
		<constructor>
			<body>
				<![CDATA[
				this.nr_of_fields = 0;
				]]>
			</body>
		</constructor>
		<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[
									this.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[
									this.nr_of_fields += 1
									]]>
								</script>
							</transition>
						</state>
						<state id="check_nr_of_fields">
							<transition target="../stopped" cond="this.nr_of_fields == 0">
                                <raise event="stop" />								
							</transition>
							<transition target="../waiting" cond="this.nr_of_fields != 0"/>
						</state>
                        <state id="stopped" />
					</state>
                    <transition target="../stopped" event="stop">
                        <script>
                            <![CDATA[
                            //NOTE: ideally, this should be ui.close_window(ui.window). we can't do this though 
                            //		  due to a security measure preventing scripts from closing pages they didn't
                            //		  open themselves
                            ui.println('refresh to play again');
                            ]]>
                        </script>
                    </transition>
				</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>
		<constructor>
			<body>
				<![CDATA[
				this.field_window = ui.new_window(400,450);
				this.canvas	= ui.append_canvas(this.field_window,400,400,{'background':'#eee'});
				ui.bind_event(this.field_window, ui.EVENTS.WINDOW_CLOSE, this.controller, 'window_close', this.inports['field_ui']);
				ui.bind_event(this.field_window, ui.EVENTS.KEY_PRESS, this.controller, 'key_press', this.inports['field_ui']);
				ui.bind_event(this.canvas.element, ui.EVENTS.MOUSE_RIGHT_CLICK, this.controller, 'right_click', this.inports['field_ui']);
				ui.bind_event(this.canvas.element, ui.EVENTS.MOUSE_MOVE, this.controller, 'mouse_move');
				ui.bind_event(this.canvas.element, ui.EVENTS.MOUSE_RELEASE, this.controller, 'mouse_release');
				]]>
			</body>
		</constructor>
		<destructor>
			<body>
				<![CDATA[
				ui.close_window(this.field_window);
				]]>
			</body>
		</destructor>
		<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[
							this.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"' /><!-- if creation parameters are supplied, it is also required to give a class name -->
							<parameter expr="this" />
							<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="field_ui" event="window_close" target="../deleting">
						<raise event="delete_instance" scope="cd">
							<parameter expr='"buttons"' />
						</raise>
						<raise event="delete_instance" scope="cd">
							<parameter expr='"balls"' />
						</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"' /><!-- if creation parameters are supplied, it is also required to give a class name -->
									<parameter expr="this.canvas" />
									<parameter expr="x" />
									<parameter expr="y" />
								</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>
                    <state id="deleting_balls_behaviour" initial="listening">
                        <state id="listening">
                            <transition port="field_ui" event="key_press" target="." cond="key == ui.KEYCODES.DELETE">
                                <parameter name="key" />
                                <raise event="delete_self" scope="narrow" target="'balls'" />
                            </transition>
                        </state>
                    </state>
				</parallel>
				<state id="deleting">
					<transition target="../deleted">
						<raise event="delete_field" scope="narrow" target="'parent'">
							<parameter expr='this.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>
        <inport name="button_ui"/>
		<constructor>
			<parameter name="parent" type="Field" />
			<parameter name="event_name" type="str" />
			<parameter name="button_text" type="str" />
			<body>
				<![CDATA[
				this.event_name = event_name;
				var button = ui.append_button(parent.field_window, event_name);
				ui.bind_event(button.element, ui.EVENTS.MOUSE_CLICK, this.controller, 'mouse_click', this.inports["button_ui"]);
				]]>
			</body>
		</constructor>
		<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='button_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="this.event_name" />
					</raise>
				</transition>
			</state>
		</scxml>
	</class>
	
	<class name="Ball">
		<atrribute name="element" />
		<attribute name="canvas" />
		<inport name="ball_ui" />
		<relationships>
			<association name="parent" class="Field" min="1" max="1" />
		</relationships>
		<constructor>
			<parameter name="canvas" />
			<parameter name="x" />
			<parameter name="y" />
			<body>
				<![CDATA[
				this.canvas 		= canvas;
				this.r 				= 20.0;
				this.vel 			= {'x': utils.random() * 3.0, 'y': utils.random() * 3.0};
				this.mouse_pos		= {};
				this.smooth 		= 0.4; // value between 0 and 1

				var circle = this.canvas.add_circle(x, y, this.r, {'fill':'#000'});
				ui.bind_event(circle, ui.EVENTS.MOUSE_PRESS, this.controller, 'mouse_press', this.inports["ball_ui"]);
				this.element = circle;
				]]>
			</body>
		</constructor>
		<destructor>
			<body>
				<![CDATA[
				this.canvas.remove_element(this.element);
				]]>
			</body>
		</destructor>
		<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[
							this.association_name = association_name;
							]]>							
						</script>
					</transition>
				</state>
				<state id="bouncing">
					<transition after="0.05" target=".">
						<script>
							<![CDATA[
							var pos = this.element.get_position();	
							if (pos.x-this.r <= 0 || pos.x+this.r >= this.canvas.width) 
								this.vel.x = -this.vel.x;
							if (pos.y-this.r <= 0 || pos.y+this.r >= this.canvas.height) 
								this.vel.y = -this.vel.y;
							this.element.move(this.vel.x, this.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[
							this.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[
							var dx = x - this.mouse_pos.x;
							var dy = y - this.mouse_pos.y;

							this.element.move(dx, dy);

							// keep ball within boundaries
							var pos = this.element.get_position();
							if (pos.x-this.r <= 0) 
								pos.x = this.r + 1;
							else if (pos.x+this.r >= this.canvas.width) 
								pos.x = this.canvas.width-this.r-1;
							if (pos.y-this.r <= 0) 
								pos.y = this.r + 1;
							else if (pos.y+this.r >= this.canvas.height) 
								pos.y = this.canvas.height-this.r-1;
							this.element.set_position(pos.x, pos.y);
							this.mouse_pos = {'x':x, 'y':y};
							this.vel = {
								'x': (1-this.smooth)*dx + this.smooth*this.vel.x,
								'y': (1-this.smooth)*dy + this.smooth*this.vel.y
							};
							]]>
						</script>
					</transition>
					<transition port="ui" event="mouse_release" target="../bouncing">
						<parameter name="x" />
						<parameter name="y" />
						<script>
							<![CDATA[
							this.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[
							this.mouse_pos = {'x':x, 'y':y};
							]]>
						</script>
					</transition>
                    <transition event="delete_self" target='../../deleted'>					
                        <raise event="delete_ball" scope="narrow" target="'parent'">
                            <parameter expr='this.association_name' />
                        </raise>
                    </transition>
				</state>
			</state>
			<state id='deleted' />
		</scxml>
	</class>
</diagram>
