# Generic Generator by Joeri Exelmans
#
# Visits SCCD-domain constructs (see sccd_constructs.py) and converts them
# to a generic language AST (see generic_language_constructs.py), that can
# then be visited by a target language writer.

import traceback

import time
from utils import Enum, Logger

from visitor import Visitor
from sccd_constructs import FormalParameter
from stateful_writer import StatefulWriter
import generic_language_constructs as GLC

Platforms = Enum("Threads","GameLoop","EventLoop") 

class GenericGenerator(Visitor):
	
	def __init__(self, platform):
		self.platform = platform
		self.writer = StatefulWriter()

	def generic_visit(self, node):
		Logger.showWarning("GenericGenerator has no visit method for node of type '" + str(type(node)) + "'.")

	def get(self):
		return self.writer.get()

	def visit_ClassDiagram(self, class_diagram):
		header = ("Generated by Statechart compiler by Glenn De Jonghe and Joeri Exelmans\n"
		          "\n"
		          "Date:   " + time.asctime() + "\n")
		if class_diagram.name or class_diagram.author or class_diagram.description:
			header += "\n"
		if class_diagram.author:
			header += "Model author: " + class_diagram.author + "\n"
		if class_diagram.name:
			header += "Model name:   " + class_diagram.name + "\n"
		if class_diagram.description.strip():
			header += "Model description:\n"
			header += class_diagram.description.strip()

		self.writer.addMultiLineComment(header)
		self.writer.addVSpace()
		self.writer.addInclude(([GLC.RuntimeModuleIdentifier(), "statecharts_core"]))
		if class_diagram.top.strip():
			self.writer.addRawCode(class_diagram.top)
		self.writer.addVSpace()

		self.writer.beginPackage(class_diagram.name)
		
		#visit children
		for c in class_diagram.classes :
			c.accept(self)
		 
		self.writer.beginClass("ObjectManager", ["ObjectManagerBase"])

		self.writer.beginConstructor()
		self.writer.addFormalParameter("controller")
		self.writer.beginMethodBody()
		self.writer.beginSuperClassConstructorCall("ObjectManagerBase")
		self.writer.addActualParameter("controller")
		self.writer.endSuperClassConstructorCall()
		self.writer.endMethodBody()
		self.writer.endConstructor()

		self.writer.beginMethod("instantiate")
		self.writer.addFormalParameter("class_name")
		self.writer.addFormalParameter("construct_params")
		self.writer.beginMethodBody()
		for index,c in enumerate(class_diagram.classes):
			self.writer.beginElseIf(GLC.EqualsExpression("class_name", GLC.String(c.name)))
			if c.isAbstract():
				# cannot instantiate abstract class
				self.writer.add(GLC.ThrowExceptionStatement(GLC.String("Cannot instantiate abstract class \"" + c.name + "\" with unimplemented methods \"" + "\", \"".join(c.abstract_method_names) + "\".")))
			else:
				new_expr = GLC.NewExpression(c.name, [GLC.SelfProperty("controller")])
				param_count = 0
				for p in c.constructors[0].parameters:
					new_expr.getActualParameters().add(GLC.ArrayIndexedExpression("construct_params", str(param_count)))
					param_count += 1
				self.writer.addAssignment(
					GLC.LocalVariableDeclaration("instance"),
					new_expr)
				self.writer.addAssignment(
					GLC.Property("instance", "associations"),
					GLC.MapExpression())
				for a in c.associations:
					a.accept(self)
			self.writer.endElseIf()
		self.writer.add(GLC.ReturnStatement("instance"))
		self.writer.endMethodBody()
		self.writer.endMethod()
		self.writer.endClass() # ObjectManager

		if self.platform == Platforms.Threads:
			controller_sub_class = "ThreadsControllerBase"
		if self.platform == Platforms.EventLoop :
			controller_sub_class = "EventLoopControllerBase"
		elif self.platform == Platforms.GameLoop :
			controller_sub_class = "GameLoopControllerBase"

		self.writer.beginClass("Controller", [controller_sub_class])
		self.writer.beginConstructor()
		for p in class_diagram.default_class.constructors[0].parameters:
			p.accept(self)
		if self.platform == Platforms.EventLoop:
			self.writer.addFormalParameter("event_loop_callbacks")
			self.writer.addFormalParameter("finished_callback", GLC.NoneExpression())
		elif self.platform == Platforms.Threads:
			self.writer.addFormalParameter("keep_running", GLC.TrueExpression())
		self.writer.beginMethodBody()
		self.writer.beginSuperClassConstructorCall(controller_sub_class)
		self.writer.addActualParameter(GLC.NewExpression("ObjectManager", [GLC.SelfExpression()]))
		if self.platform == Platforms.EventLoop:
			self.writer.addActualParameter("event_loop_callbacks")
			self.writer.addActualParameter("finished_callback")
		elif self.platform == Platforms.Threads:
			self.writer.addActualParameter("keep_running")
		self.writer.endSuperClassConstructorCall()
		for i in class_diagram.inports:
			self.writer.add(GLC.FunctionCall(GLC.SelfProperty("addInputPort"), [GLC.String(i)]))
		for o in class_diagram.outports:
			self.writer.add(GLC.FunctionCall(GLC.SelfProperty("addOutputPort"), [GLC.String(o)]))
		actual_parameters = [p.getIdent() for p in class_diagram.default_class.constructors[0].parameters]
		self.writer.add(GLC.FunctionCall(GLC.Property(GLC.SelfProperty("object_manager"), "createInstance"), [GLC.String(class_diagram.default_class.name), GLC.ArrayExpression(actual_parameters)]))
		self.writer.endMethodBody()
		self.writer.endConstructor()
		self.writer.endClass() # Controller

		# Visit test node if there is one
		if class_diagram.test:
			class_diagram.test.accept(self)

		self.writer.endPackage()

	def visit_DiagramTest(self, test):
		# helper class
		self.writer.beginClass("InputEvent")
		self.writer.beginConstructor()
		self.writer.addFormalParameter("name")
		self.writer.addFormalParameter("port")
		self.writer.addFormalParameter("parameters")
		self.writer.addFormalParameter("time_offset")
		self.writer.beginMethodBody()
		self.writer.addAssignment(GLC.SelfProperty("name"), "name")
		self.writer.addAssignment(GLC.SelfProperty("port"), "port")
		self.writer.addAssignment(GLC.SelfProperty("parameters"), "parameters")
		self.writer.addAssignment(GLC.SelfProperty("time_offset"), "time_offset")
		self.writer.endMethodBody()
		self.writer.endConstructor()
		self.writer.endClass()
		self.writer.beginClass("Test")
		if test.input:
			test.input.accept(self)
		else:
			self.writer.addStaticAttribute("input_events", GLC.ArrayExpression())
		if test.expected:
			test.expected.accept(self)
		else:
			self.writer.addStaticAttribute("expected_events", GLC.ArrayExpression())
		self.writer.endClass()

	def visit_DiagramTestInput(self, test_input):
		# write array of input events
		self.writer.startRecordingExpression()
		self.writer.beginArray()
		for e in test_input.input_events:
			e.accept(self)
		self.writer.endArray()
		array_expr = self.writer.stopRecordingExpression()
		self.writer.addStaticAttribute("input_events", array_expr)

	def visit_DiagramTestInputEvent(self, event):
		self.writer.add(GLC.NewExpression("InputEvent", [GLC.String(event.name), GLC.String(event.port), GLC.ArrayExpression(event.parameters), event.time]))

	def visit_DiagramTestExpected(self, test_expected):
		# write array of slots containing expected events
		self.writer.startRecordingExpression()
		self.writer.beginArray()
		for s in test_expected.slots:
			s.accept(self)
		self.writer.endArray()
		array_expr = self.writer.stopRecordingExpression()
		self.writer.addStaticAttribute("expected_events", array_expr)

	def visit_DiagramTestExpectedSlot(self, slot):
		# write slot
		self.writer.beginArray()
		for e in slot.expected_events:
			e.accept(self)
		self.writer.endArray()

	def visit_DiagramTestEvent(self, event):
		self.writer.add(GLC.NewExpression("Event", [GLC.String(event.name), GLC.String(event.port), GLC.ArrayExpression(event.parameters)]))

	def visit_Class(self, class_node):
		"""
		Generate code for Class construct
		"""

		super_classes = []
		if not class_node.super_class_objs:
			# if none of the class' super classes is defined in the diagram,
			# we have to inherit RuntimeClassBase
			if class_node.statechart:
				# only inherit RuntimeClassBase if class has a statechart
				super_classes.append("RuntimeClassBase")
		if class_node.super_classes:
			for super_class in class_node.super_classes:
				super_classes.append(super_class)

		self.writer.beginClass(class_node.name, super_classes)

		#visit constructor
		for i in class_node.constructors :
			i.accept(self)

		self.writer.beginMethod("user_defined_constructor")
		for p in class_node.constructors[0].getParams():
			p.accept(self)
		self.writer.beginMethodBody()
		for super_class in class_node.super_classes:
			# begin call
			if super_class in class_node.super_class_objs:
				self.writer.beginSuperClassMethodCall(super_class, "user_defined_constructor")
			else:
				self.writer.beginSuperClassConstructorCall(super_class)
			# write actual parameters
			if super_class in class_node.constructors[0].super_class_parameters:
				for p in class_node.constructors[0].super_class_parameters[super_class]:
					self.writer.addActualParameter(p)
			# end call
			if super_class in class_node.super_class_objs:
				self.writer.endSuperClassMethodCall()
			else:
				self.writer.endSuperClassConstructorCall()
		self.writer.addRawCode(class_node.constructors[0].body)
		self.writer.endMethodBody()
		self.writer.endMethod()


		#visit children
		for i in class_node.destructors :
			i.accept(self)
		for i in class_node.methods :
			i.accept(self)

		if class_node.statechart:
			self.writer.beginMethod("initializeStatechart")
			self.writer.beginMethodBody()

			for c in class_node.statechart.composites :
				self.writer.addAssignment(GLC.MapIndexedExpression(GLC.SelfProperty("current_state"), GLC.SelfProperty(c.full_name)), GLC.ArrayExpression())

			if class_node.statechart.histories:
				self.writer.addVSpace()
				for node in class_node.statechart.combined_history_parents:
					self.writer.addAssignment(GLC.MapIndexedExpression(GLC.SelfProperty("history_state"), GLC.SelfProperty(node.full_name)), GLC.ArrayExpression())

			self.writer.addVSpace()
			self.writer.addComment("Enter default state")	
			for default_node in class_node.statechart.root.defaults:
				if default_node.is_composite:
					self.writer.add(GLC.FunctionCall(GLC.SelfProperty("enterDefault_"+default_node.full_name)))
				elif default_node.is_basic:
					self.writer.add(GLC.FunctionCall(GLC.SelfProperty("enter_"+default_node.full_name)))
			self.writer.endMethodBody()
			self.writer.endMethod()

			class_node.statechart.accept(self)

		self.writer.endClass()


	def visit_FormalParameter(self, formal_parameter):
		self.writer.addFormalParameter(formal_parameter.getIdent(), formal_parameter.getDefault())
		
	def visit_Constructor(self, constructor):
		self.writer.beginConstructor()
		if constructor.parent_class.statechart:
			self.writer.addFormalParameter("controller")
		for p in constructor.getParams():
			self.writer.addFormalParameter(p.getIdent(), p.getDefault())
		self.writer.beginMethodBody() # constructor body

		if constructor.parent_class.statechart:
			self.writer.beginSuperClassConstructorCall("RuntimeClassBase")
			self.writer.addActualParameter("controller")
			self.writer.endSuperClassConstructorCall()

			self.writer.addVSpace()

			if constructor.parent_class.statechart.big_step_maximality == "take_one":
				self.writer.addAssignment(GLC.Property(GLC.SelfProperty("semantics"), "big_step_maximality"), GLC.Property("StatechartSemantics", "TakeOne"))
			elif constructor.parent_class.statechart.big_step_maximality == "take_many":
				self.writer.addAssignment(GLC.Property(GLC.SelfProperty("semantics"), "big_step_maximality"), GLC.Property("StatechartSemantics", "TakeMany"))

			if constructor.parent_class.statechart.internal_event_lifeline == "queue":
				self.writer.addAssignment(GLC.Property(GLC.SelfProperty("semantics"), "internal_event_lifeline"), GLC.Property("StatechartSemantics", "Queue"))
			elif constructor.parent_class.statechart.internal_event_lifeline == "next_small_step":
				self.writer.addAssignment(GLC.Property(GLC.SelfProperty("semantics"), "internal_event_lifeline"), GLC.Property("StatechartSemantics", "NextSmallStep"))
			elif constructor.parent_class.statechart.internal_event_lifeline == "next_combo_step":
				self.writer.addAssignment(GLC.Property(GLC.SelfProperty("semantics"), "internal_event_lifeline"), GLC.Property("StatechartSemantics", "NextComboStep"))

			if constructor.parent_class.statechart.input_event_lifeline == "first_small_step":
				self.writer.addAssignment(GLC.Property(GLC.SelfProperty("semantics"), "input_event_lifeline"), GLC.Property("StatechartSemantics", "FirstSmallStep"))
			elif constructor.parent_class.statechart.input_event_lifeline == "first_combo_step":
				self.writer.addAssignment(GLC.Property(GLC.SelfProperty("semantics"), "input_event_lifeline"), GLC.Property("StatechartSemantics", "FirstComboStep"))
			elif constructor.parent_class.statechart.input_event_lifeline == "whole":
				self.writer.addAssignment(GLC.Property(GLC.SelfProperty("semantics"), "input_event_lifeline"), GLC.Property("StatechartSemantics", "Whole"))

			if constructor.parent_class.statechart.priority == "source_parent":
				self.writer.addAssignment(GLC.Property(GLC.SelfProperty("semantics"), "priority"), GLC.Property("StatechartSemantics", "SourceParent"))
			elif constructor.parent_class.statechart.priority == "source_child":
				self.writer.addAssignment(GLC.Property(GLC.SelfProperty("semantics"), "priority"), GLC.Property("StatechartSemantics", "SourceChild"))


			if constructor.parent_class.statechart.concurrency == "single":
				self.writer.addAssignment(GLC.Property(GLC.SelfProperty("semantics"), "concurrency"), GLC.Property("StatechartSemantics", "Single"))
			elif constructor.parent_class.statechart.concurrency == "many":
				self.writer.addAssignment(GLC.Property(GLC.SelfProperty("semantics"), "concurrency"), GLC.Property("StatechartSemantics", "Many"))

		for p in constructor.parent_class.inports:
			self.writer.addAssignment(
				GLC.MapIndexedExpression(GLC.SelfProperty("inports"), GLC.String(p)),
				GLC.FunctionCall(GLC.Property("controller", "addInputPort"), [GLC.String(p), GLC.SelfExpression()]))

		if constructor.parent_class.attributes:
			self.writer.addVSpace()
			self.writer.addComment("User defined attributes")
			for attribute in constructor.parent_class.attributes:
				if attribute.init_value is None :
					self.writer.addAssignment(GLC.SelfProperty(attribute.name), GLC.NoneExpression())
				else :
					self.writer.addAssignment(GLC.SelfProperty(attribute.name), attribute.init_value)

		self.writer.addVSpace()
		self.writer.addComment("Call user defined constructor")
		self.writer.beginSuperClassMethodCall(constructor.parent_class.name, "user_defined_constructor")
		for p in constructor.getParams():
			# we can't do p.accept(self) here because 'p' is a FormalParameter
			# and we want to write it as an actual parameter
			self.writer.addActualParameter(p.getIdent())
		self.writer.endSuperClassMethodCall()
		self.writer.endMethodBody()
		self.writer.endConstructor()

	def visit_Destructor(self, destructor):
		self.writer.beginMethod("user_defined_destructor")
		self.writer.beginMethodBody()
		if destructor.body.strip():
			self.writer.addRawCode(destructor.body)
		if destructor.parent_class.super_classes:
			self.writer.addComment("Call super class destructors")
			for super_class in destructor.parent_class.super_classes:
				# begin call
				if super_class in destructor.parent_class.super_class_objs:
					self.writer.beginSuperClassMethodCall(super_class, "user_defined_destructor")
					self.writer.endSuperClassMethodCall()
				else:
					self.writer.beginSuperClassDestructorCall(super_class)
					self.writer.endSuperClassDestructorCall()
					pass

				#self.writer.beginSuperClassMethodCall(super_class, "user_defined_destructor")
				#self.writer.endSuperClassMethodCall()
		self.writer.endMethodBody()
		self.writer.endMethod()
		
	def visit_Method(self, method):
		self.writer.addVSpace()
		self.writer.beginMethod(method.name, "User defined method")
		for p in method.parameters:
			p.accept(self)
		self.writer.beginMethodBody()
		self.writer.addRawCode(method.body)
		self.writer.endMethodBody()
		self.writer.endMethod()
		
	def visit_Association(self, association):
		self.writer.addAssignment(
			GLC.MapIndexedExpression(
				GLC.Property("instance", "associations"),
				GLC.String(association.name)),
			GLC.NewExpression("Association", [GLC.String(association.to_class), str(association.min), str(association.max)]))

	#helper method
	def writeTransitionsRecursively(self, current_node):
		valid_children = []
		for child in current_node.children :
			if child.is_composite or child.is_basic :
				valid_children.append(child)

		has_candidates_children = (len(valid_children) > 0)
		has_candidates_current = (len(current_node.transitions) > 0)

		if has_candidates_children:
			self.writer.beginMethod("generateCandidatesChildren_" + current_node.full_name)
			self.writer.beginMethodBody()
			if current_node.is_parallel_state:
				self.writer.addAssignment(
					GLC.LocalVariableDeclaration("branch_done"),
					GLC.FalseExpression())
				for child in valid_children :
					self.writer.addAssignment(
						"branch_done",
						GLC.OrExpression(
							GLC.FunctionCall(GLC.SelfProperty("generateCandidates_" + child.full_name)),
							"branch_done"))
				self.writer.add(GLC.ReturnStatement("branch_done"))
			elif current_node.is_composite:
				for i, child in enumerate(valid_children) :
					self.writer.beginElseIf(GLC.EqualsExpression(
						GLC.ArrayIndexedExpression(
							GLC.MapIndexedExpression(
								GLC.SelfProperty("current_state"),
								GLC.SelfProperty(current_node.full_name)),
							"0"),
						GLC.SelfProperty(child.full_name)))
					self.writer.add(GLC.ReturnStatement(GLC.FunctionCall(GLC.SelfProperty("generateCandidates_"+child.full_name))))
					self.writer.endElseIf()
				self.writer.add(GLC.ReturnStatement(GLC.FalseExpression()))
			self.writer.endMethodBody()
			self.writer.endMethod()

		if has_candidates_current:
			self.writer.beginMethod("generateCandidatesCurrent_" + current_node.full_name)
			self.writer.beginMethodBody()
			self.writeFromTransitions(current_node)
			self.writer.add(GLC.ReturnStatement(GLC.FalseExpression()))
			self.writer.endMethodBody()
			self.writer.endMethod()

		self.writer.beginMethod("generateCandidates_" + current_node.full_name)
		self.writer.beginMethodBody()

		if not has_candidates_children and not has_candidates_current:
			self.writer.add(GLC.ReturnStatement(GLC.FalseExpression()))
		else:
			self.writer.beginIf(
				GLC.NotExpression(GLC.FunctionCall(
					GLC.Property(GLC.SelfProperty("combo_step"), "isArenaChanged"),
					[GLC.SelfProperty(current_node.full_name)])))

			if has_candidates_children and has_candidates_current:
				self.writer.addAssignment(
					GLC.LocalVariableDeclaration("branch_done"),
					GLC.FalseExpression())

			if not has_candidates_children and has_candidates_current:
				self.writer.add(GLC.ReturnStatement(GLC.FunctionCall(GLC.SelfProperty("generateCandidatesCurrent_" + current_node.full_name))))
			elif not has_candidates_current and has_candidates_children:
				self.writer.add(GLC.ReturnStatement(GLC.FunctionCall(GLC.SelfProperty("generateCandidatesChildren_" + current_node.full_name))))
			else:
				self.writer.beginElseIf(GLC.EqualsExpression(
					GLC.Property(GLC.SelfProperty("semantics"), "priority"),
					GLC.Property("StatechartSemantics", "SourceParent")))
				if has_candidates_current:
					self.writer.addAssignment("branch_done", GLC.FunctionCall(GLC.SelfProperty("generateCandidatesCurrent_" + current_node.full_name)))
				if has_candidates_children:
					self.writer.beginIf(GLC.NotExpression("branch_done"))
					self.writer.addAssignment("branch_done", GLC.FunctionCall(GLC.SelfProperty("generateCandidatesChildren_" + current_node.full_name)))
					self.writer.endIf()
				self.writer.endElseIf()
				self.writer.beginElseIf(GLC.EqualsExpression(
					GLC.Property(GLC.SelfProperty("semantics"), "priority"),
					GLC.Property("StatechartSemantics", "SourceChild")))
				if has_candidates_children:
					self.writer.addAssignment("branch_done", GLC.FunctionCall(GLC.SelfProperty("generateCandidatesChildren_" + current_node.full_name)))
				if has_candidates_current:
					self.writer.beginIf(GLC.NotExpression("branch_done"))
					self.writer.addAssignment("branch_done", GLC.FunctionCall(GLC.SelfProperty("generateCandidatesCurrent_" + current_node.full_name)))
					self.writer.endIf()
				self.writer.endElseIf()

			if has_candidates_children and has_candidates_current:
				self.writer.add(GLC.ReturnStatement("branch_done"))
			self.writer.endIf()
			self.writer.beginElse()
			self.writer.add(GLC.ReturnStatement(GLC.TrueExpression()))
			self.writer.endElse()

		self.writer.endMethodBody()
		self.writer.endMethod()

		for index, transition in enumerate(current_node.transitions, start=1):
			self.writeTransitionAction(transition, index)
		
		for child in valid_children :
			self.writeTransitionsRecursively(child)
				
	#helper method
	def writeFromTransitions(self, current_node): 
		# get all transition out of this state
		out_transitions = current_node.transitions
		if len(out_transitions) == 0 :
			return
		
		for index, transition in enumerate(out_transitions, start=1):
			self.writeTransitionCondition(transition, index)
		
	def visit_FormalEventParameter(self, formal_event_parameter):
		self.writer.add(formal_event_parameter.name)
		
	def writeFormalEventParameters(self, transition):
		parameters = transition.getTrigger().getParameters()
		if(len(parameters) > 0) :
			for index, parameter in enumerate(parameters):
				self.writer.startRecordingExpression()
				parameter.accept(self)
				parameter_expr = self.writer.stopRecordingExpression()
				self.writer.addAssignment(
					GLC.LocalVariableDeclaration(parameter_expr),
					GLC.ArrayIndexedExpression("parameters", str(index)))
		
		
	def writeTransitionAction(self, transition, index):
		self.writer.beginMethod("transition_" + transition.parent_node.full_name + "_" + str(index))
		self.writer.addFormalParameter("parameters")
		self.writer.beginMethodBody()

		# handle parameters to actually use them
		self.writeFormalEventParameters(transition)
		
		exits = transition.getExitNodes()
		
		# write exit actions
		if not exits[-1].is_basic:
			self.writer.add(GLC.FunctionCall(GLC.SelfProperty("exit_"+exits[-1].full_name)))
		else:
			for node in exits:
				if node.is_basic:
					self.writer.add(GLC.FunctionCall(GLC.SelfProperty("exit_"+node.full_name)))
					
		# write trigger actions
		transition.getAction().accept(self)

		# add arena of transition to list of 'changed' states,
		# this may prevent other transitions whose arenas overlap to be taken
		self.writer.add(
			GLC.FunctionCall(
				GLC.Property(GLC.SelfProperty("combo_step"), "setArenaChanged"),
				[GLC.SelfProperty(transition.arena.full_name)]))

		# write enter actions
		for (entering_node, is_ending_node) in transition.getEnterNodes() : 
			if is_ending_node :
				if entering_node.is_composite:
					self.writer.add(GLC.FunctionCall(GLC.SelfProperty("enterDefault_" + entering_node.full_name)))
				elif entering_node.is_history:
					if (entering_node.is_history_deep) :
						self.writer.add(GLC.FunctionCall(GLC.SelfProperty("enterHistoryDeep_" + entering_node.parent.full_name)))
					else :
						self.writer.add(GLC.FunctionCall(GLC.SelfProperty("enterHistoryShallow_" + entering_node.parent.full_name)))
				else:
					self.writer.add(GLC.FunctionCall(GLC.SelfProperty("enter_" + entering_node.full_name)))
			else :
				if entering_node.is_composite:
					self.writer.add(GLC.FunctionCall(GLC.SelfProperty("enter_" + entering_node.full_name)))

		self.writer.endMethodBody()
		self.writer.endMethod()
						
	def writeTransitionCondition(self, transition, index):
		trigger = transition.getTrigger()

		self.writer.addAssignment(
			GLC.LocalVariableDeclaration("enabled_events"),
			GLC.FunctionCall(GLC.SelfProperty("getEnabledEvents")))

		if not trigger.isUC():
			self.writer.beginForLoopIterateArray("enabled_events", "e")
			condition = GLC.EqualsExpression(
				GLC.Property(GLC.ForLoopCurrentElement("enabled_events", "e"), "name"),
					GLC.String(trigger.getEvent()))
			if trigger.getPort() != "":
				condition = GLC.AndExpression(
					condition,
					GLC.EqualsExpression(
						GLC.Property(GLC.ForLoopCurrentElement("enabled_events", "e"), "port"),
						GLC.String(trigger.getPort())))
			self.writer.beginIf(condition)
		# evaluate guard
		if transition.hasGuard() :
			# handle parameters for guard evaluation
			if not transition.getTrigger().isUC():
				self.writer.addAssignment(GLC.LocalVariableDeclaration("parameters"), GLC.Property(GLC.ForLoopCurrentElement("enabled_events", "e"), "parameters"))
				self.writeFormalEventParameters(transition)
			self.writer.startRecordingExpression()
			transition.getGuard().accept(self) # --> visit_Expression
			expr = self.writer.stopRecordingExpression()
			self.writer.beginIf(expr)

		if trigger.isUC():
			params_expr = GLC.ArrayExpression()
		else:
			params_expr = GLC.Property(GLC.ForLoopCurrentElement("enabled_events", "e"), "parameters")
		self.writer.add(GLC.FunctionCall(GLC.Property(GLC.SelfProperty("small_step"), "addCandidate"), [GLC.SelfProperty("transition_" + transition.parent_node.full_name + "_" + str(index)), params_expr]))

		self.writer.add(GLC.ReturnStatement(GLC.TrueExpression()))

		if transition.hasGuard() :
			self.writer.endIf()
		if not trigger.isUC() :
			self.writer.endIf()
			self.writer.endForLoopIterateArray()
	
	def visit_EnterAction(self, enter_method):
		parent_node = enter_method.parent_node
		self.writer.beginMethod("enter_" + parent_node.full_name)
		self.writer.beginMethodBody()

		# take care of any AFTER events
		for transition in parent_node.transitions :
			trigger = transition.getTrigger()
			if trigger.isAfter() :
				self.writer.startRecordingExpression()
				trigger.after.accept(self)
				after = self.writer.stopRecordingExpression()
				
				self.writer.addAssignment(
					GLC.MapIndexedExpression(GLC.SelfProperty("timers"), str(trigger.getAfterIndex())),
					after)

		if enter_method.action:
			enter_method.action.accept(self)
		self.writer.add(
			GLC.ArrayPushBack(
					GLC.MapIndexedExpression(
						GLC.SelfProperty("current_state"),
						GLC.SelfProperty(parent_node.parent.full_name)),
					GLC.SelfProperty(parent_node.full_name)))
		self.writer.endMethodBody()
		self.writer.endMethod()
		
	#helper method
	def writeEnterDefault(self, entered_node):
		self.writer.beginMethod("enterDefault_" + entered_node.full_name)
		self.writer.beginMethodBody()
		self.writer.add(GLC.FunctionCall(GLC.SelfProperty("enter_"+entered_node.full_name)))
		if entered_node.is_composite:
			l = entered_node.defaults
			for i in l:
				if i.is_composite:
					self.writer.add(GLC.FunctionCall(GLC.SelfProperty("enterDefault_" + i.full_name)))
				elif i.is_basic:
					self.writer.add(GLC.FunctionCall(GLC.SelfProperty("enter_" + i.full_name)))
		self.writer.endMethodBody()
		self.writer.endMethod()
		 
	def visit_ExitAction(self, exit_method):
		exited_node = exit_method.parent_node
		self.writer.beginMethod("exit_" + exited_node.full_name)
		self.writer.beginMethodBody()

		#If the exited node is composite take care of potential history and the leaving of descendants
		if exited_node.is_composite :
			#handle history
			if exited_node.save_state_on_exit :
				self.writer.addAssignment(
					GLC.MapIndexedExpression(
						GLC.SelfProperty("history_state"),
						GLC.SelfProperty(exited_node.full_name)),
					GLC.MapIndexedExpression(
						GLC.SelfProperty("current_state"),
						GLC.SelfProperty(exited_node.full_name)))
			
			#Take care of leaving children
			children = exited_node.children
			if exited_node.is_parallel_state:
				for child in children:
					if not child.is_history :
						self.writer.add(GLC.FunctionCall(GLC.SelfProperty("exit_"+child.full_name)))
			else:
				for child in children:
					if not child.is_history :
						self.writer.beginIf(GLC.ArrayContains(
								GLC.MapIndexedExpression(
									GLC.SelfProperty("current_state"),
									GLC.SelfProperty(exited_node.full_name)),
								GLC.SelfProperty(child.full_name)))
						self.writer.add(GLC.FunctionCall(GLC.SelfProperty("exit_"+child.full_name)))
						self.writer.endIf()
		
		# take care of any AFTER events
		for transition in exited_node.transitions :
			trigger = transition.getTrigger()
			if trigger.isAfter() :
				self.writer.add(GLC.MapRemoveElement(
					GLC.SelfProperty("timers"),
					str(trigger.getAfterIndex())))
				
		#Execute user-defined exit action if present
		if exit_method.action:
			exit_method.action.accept(self)
			
		#Adjust state
		self.writer.addAssignment(
			GLC.MapIndexedExpression(
				GLC.SelfProperty("current_state"),
				GLC.SelfProperty(exited_node.parent.full_name)),
			GLC.ArrayExpression()) # SPECIAL CASE FOR ORTHOGONAL??
		
		self.writer.endMethodBody()
		self.writer.endMethod()
		
			
	#helper method
	def writeEnterHistory(self, entered_node, is_deep):
		self.writer.beginMethod("enterHistory" + ("Deep" if is_deep else "Shallow") + "_" + entered_node.full_name)
		self.writer.beginMethodBody()

		self.writer.beginIf(GLC.EqualsExpression(
			GLC.ArrayLength(
				GLC.MapIndexedExpression(
					GLC.SelfProperty("history_state"),
					GLC.SelfProperty(entered_node.full_name))),
			"0"))
		"""self.writer.beginIfBlock(GLC.EqualsExpression(
			GLC.ArrayLength(
				GLC.MapIndexedExpression(
					GLC.SelfProperty("history_state"),
					GLC.SelfProperty(entered_node.full_name))),
			"0"))"""
		defaults = entered_node.defaults

		for node in defaults:
			if node.is_basic :
				self.writer.add(GLC.FunctionCall(GLC.SelfProperty("enter_"+node.full_name)))
			elif node.is_composite :
				self.writer.add(GLC.FunctionCall(GLC.SelfProperty("enterDefault_"+node.full_name)))

		self.writer.endIf()
		self.writer.beginElse()
		children = entered_node.children
		if entered_node.is_parallel_state:
			for child in children:
				if not child.is_history :
					self.writer.add(GLC.FunctionCall(GLC.SelfProperty("enter_"+child.full_name)))
					self.writer.add(GLC.FunctionCall(GLC.SelfProperty("enterHistory"+("Deep" if is_deep else "Shallow")+"_"+child.full_name)))
		else:
			for child in children:
				if not child.is_history :
					self.writer.beginIf(GLC.ArrayContains(
						GLC.MapIndexedExpression(
							GLC.SelfProperty("history_state"),
							GLC.SelfProperty(entered_node.full_name)),
						GLC.SelfProperty(child.full_name)))
					if child.is_composite:
						if is_deep :
							self.writer.add(GLC.FunctionCall(GLC.SelfProperty("enter_"+child.full_name)))
							self.writer.add(GLC.FunctionCall(GLC.SelfProperty("enterHistoryDeep_"+child.full_name)))
						else :
							self.writer.add(GLC.FunctionCall(GLC.SelfProperty("enterDefault_"+child.full_name)))
					else:
						self.writer.add(GLC.FunctionCall(GLC.SelfProperty("enter_"+child.full_name)))
					self.writer.endIf()
		self.writer.endElse()

		self.writer.endMethodBody()
		self.writer.endMethod()

	def visit_StateChart(self, statechart):

		# assign each node a unique ID
		self.writer.addVSpace()
		self.writer.addComment("Unique IDs for all statechart nodes")
		for (i,node) in enumerate(statechart.composites + statechart.basics):
			self.writer.addStaticAttribute(node.full_name, str(i))



		self.writer.addVSpace()
		self.writer.addComment("Statechart enter/exit action method(s)")
		
		#visit enter and exit action of children
		for i in statechart.composites + statechart.basics:
			if i is not statechart.root :
				i.enter_action.accept(self)
				i.exit_action.accept(self)

		# write out statecharts methods for enter/exit state
		if len(statechart.composites) > 1 :
			self.writer.addVSpace()
			self.writer.addComment("Statechart enter/exit default method(s)")
			for i in statechart.composites :
				if i is not statechart.root :
					self.writeEnterDefault(i)

		# write out statecharts methods for enter/exit history
		if statechart.histories:
			self.writer.addVSpace()
			self.writer.addComment("Statechart enter/exit history method(s)")
			for i in statechart.shallow_history_parents:
				self.writeEnterHistory(i, False)
			for i in statechart.deep_history_parents:
				self.writeEnterHistory(i, True) 

		self.writer.addVSpace()
		self.writer.addComment("Statechart transitions")
		self.writeTransitionsRecursively(statechart.root)			
				
		# write out transition function
		self.writer.beginMethod("generateCandidates", "Generate transition candidates for current small step")
		self.writer.beginMethodBody()
		self.writer.add(GLC.FunctionCall(
				GLC.SelfProperty("generateCandidates_"+statechart.root.full_name)))
		self.writer.endMethodBody()
		self.writer.endMethod()

	def visit_SelfReference(self, self_reference):
		self.writer.add(GLC.SelfExpression())

	def visit_StateReference(self, state_ref):
		self.writer.beginArray()
		for node in state_ref.getNodes():
			self.writer.add(GLC.SelfProperty(node.full_name))
		self.writer.endArray()

	def visit_InStateCall(self, in_state_call):
		self.writer.beginFunctionCall(GLC.SelfProperty("inState"))
		self.writer.startRecordingExpression()
		in_state_call.target.accept(self)
		expr = self.writer.stopRecordingExpression()
		self.writer.addActualParameter(expr)
		self.writer.endFunctionCall()

	def visit_Expression(self, expression):
		self.writer.startRecordingExpression()
		self.writer.beginGlue()
		for part in expression.expression_parts:
			part.accept(self)
		self.writer.endGlue()
		expr = self.writer.stopRecordingExpression()
		self.writer.add(expr)

	def visit_ExpressionPartString(self, e):
		self.writer.add(e.string)
		
	def visit_RaiseEvent(self, raise_event):
		self.writer.startRecordingExpression()
		self.writer.begin(GLC.NewExpression("Event"))

		self.writer.addActualParameter(GLC.String(raise_event.getEventName()))
		if raise_event.isOutput():
			self.writer.addActualParameter(GLC.String(raise_event.getPort()))
		else:
			self.writer.addActualParameter(GLC.NoneExpression())

		self.writer.end()
		new_event_expr = self.writer.stopRecordingExpression()

		self.writer.startRecordingExpression()
		self.writer.beginArray()
		if raise_event.isCD():
			self.writer.add(GLC.SelfExpression())
		for param in raise_event.getParameters() :
			param.accept(self) # -> visit_Expression will cause expressions to be added to array
		self.writer.endArray()
		parameters_array_expr = self.writer.stopRecordingExpression()
		new_event_expr.getActualParameters().add(parameters_array_expr)

		if raise_event.isNarrow():
			self.writer.add(GLC.FunctionCall(
				GLC.Property(GLC.SelfProperty("big_step"), "outputEventOM"), [
					GLC.NewExpression("Event", [
						GLC.String("narrow_cast"),
						GLC.NoneExpression(),
						GLC.ArrayExpression([
							GLC.SelfExpression(),
							raise_event.getTarget(),
							new_event_expr])])]))
		elif raise_event.isLocal():
			self.writer.add(GLC.FunctionCall(
				GLC.SelfProperty("raiseInternalEvent"),
				[new_event_expr]))
		elif raise_event.isOutput():
			self.writer.add(GLC.FunctionCall(
				GLC.Property(GLC.SelfProperty("big_step"), "outputEvent"),
				[new_event_expr]))
		elif raise_event.isCD():
			self.writer.add(GLC.FunctionCall(
				GLC.Property(GLC.SelfProperty("big_step"), "outputEventOM"),
				[new_event_expr]))
		elif raise_event.isBroad():
			self.writer.add(GLC.FunctionCall(
				GLC.Property(GLC.SelfProperty("big_step"), "outputEventOM"),
				[GLC.NewExpression("Event", [
					GLC.String("broad_cast"),
					GLC.NoneExpression(),
					GLC.ArrayExpression([
						new_event_expr])])]))
			
	def visit_Script(self, script):
		self.writer.addRawCode(script.code)
		
	def visit_Log(self, log):
		self.writer.add(GLC.LogStatement(log.message))
		
	def visit_Assign(self, assign):
		self.writer.startRecordingExpression()
		assign.lvalue.accept(self) # --> visit_Expression
		lvalue = self.writer.stopRecordingExpression()
		self.writer.startRecordingExpression()
		assign.expression.accept(self) # --> visit_Expression
		rvalue = self.writer.stopRecordingExpression()
		self.writer.addAssignment(lvalue, rvalue)

