include "primitives.alh"
include "modelling.alh"
include "object_operations.alh"
include "utils.alh"

Boolean function main(model : Element):
	Element elements
	String class
	Element attrs
	Element attr_keys
	String attr_key
	String group
	String elem
	Integer loc_x
	Integer loc_y
	Integer text_loc
	loc_x = 10
	loc_y = 10

	Element to_remove
	String elem_to_remove
	Element groups
	Element class_types
	Element metamodel
	metamodel = model["metamodel"]
	String class_type

	// Construct our own kind of tracability
	Element cs_to_as
	Element as_to_cs
	String asid
	cs_to_as = dict_create()
	as_to_cs = dict_create()
	groups = allInstances(model, "rendered/Group")
	while (set_len(groups) > 0):
		group = set_pop(groups)
		asid = read_attribute(model, group, "__asid")
		dict_add(cs_to_as, group, "abstract/" + asid)
		dict_add(as_to_cs, "abstract/" + asid, group)

	// Now render everything
	groups = dict_create()
	class_types = allInstances(metamodel, "Class")
	while (set_len(class_types) > 0):
		class_type = set_pop(class_types)

		if (string_startswith(class_type, "abstract/")):
			elements = allInstances(model, class_type)

			while (set_len(elements) > 0):
				class = set_pop(elements)

				if (is_edge(model["model"][class])):
					continue!
				
				Integer x
				Integer y
				x = loc_x
				y = loc_y

				// Check if there is already an associated element
				if (dict_in(as_to_cs, class)):
					// Yes, but is it still clean?
					Element related_groups
					group = as_to_cs[class]

					if (bool_not(read_attribute(model, group, "dirty"))):
						dict_add(groups, class, group)
						continue!
					else:
						group = as_to_cs[class]
						to_remove = allAssociationDestinations(model, group, "rendered/contains")
						x = create_value(read_attribute(model, group, "x"))
						y = create_value(read_attribute(model, group, "y"))

						while (set_len(to_remove) > 0):
							elem_to_remove = set_pop(to_remove)
							if (read_type(model, elem_to_remove) == "rendered/Group"):
								set_add(to_remove, elem_to_remove)
							else:
								model_delete_element(model, elem_to_remove)
						model_delete_element(model, group)
						dict_delete(as_to_cs, class)

				if (dict_in(groups, class)):
					// Already rendered this, so skip
					continue!

				text_loc = 5

				group = instantiate_node(model, "rendered/Group", "")
				instantiate_attribute(model, group, "x", x)
				instantiate_attribute(model, group, "y", y)
				instantiate_attribute(model, group, "__asid", list_read(string_split_nr(class, "/", 1), 1))
				instantiate_attribute(model, group, "layer", 0)
				dict_add(groups, class, group)

				loc_x = loc_x + 250
				if (loc_x > 2000):
					loc_x = 10
					loc_y = loc_y + 300

				elem = instantiate_node(model, "rendered/Rectangle", "")
				instantiate_attribute(model, elem, "x", 0)
				instantiate_attribute(model, elem, "y", 0)
				instantiate_attribute(model, elem, "height", 40 + set_len(getAttributes(model, class)) * 20)
				instantiate_attribute(model, elem, "width", 200)
				instantiate_attribute(model, elem, "lineWidth", 2) 
				instantiate_attribute(model, elem, "lineColour", "black")
				instantiate_attribute(model, elem, "fillColour", "white")
				instantiate_attribute(model, elem, "layer", 1)
				instantiate_link(model, "rendered/contains", "", group, elem)

				elem = instantiate_node(model, "rendered/Text", "")
				instantiate_attribute(model, elem, "x", 5)
				instantiate_attribute(model, elem, "y", 3)
				instantiate_attribute(model, elem, "lineWidth", 1)
				instantiate_attribute(model, elem, "lineColour", "black")
				instantiate_attribute(model, elem, "text", string_join(cast_value(list_read(string_split_nr(class, "/", 1), 1)), " : " + cast_value(list_read(string_split_nr(read_type(model, class), "/", 1), 1))))
				instantiate_attribute(model, elem, "layer", 2)
				instantiate_link(model, "rendered/contains", "", group, elem)

				elem = instantiate_node(model, "rendered/Line", "")
				instantiate_attribute(model, elem, "x", 0)
				instantiate_attribute(model, elem, "y", 20)
				instantiate_attribute(model, elem, "targetX", 200)
				instantiate_attribute(model, elem, "targetY", 20)
				instantiate_attribute(model, elem, "lineWidth", 1)
				instantiate_attribute(model, elem, "lineColour", "black")
				instantiate_attribute(model, elem, "arrow", False)
				instantiate_attribute(model, elem, "layer", 2)
				instantiate_link(model, "rendered/contains", "", group, elem)

				attrs = getAttributes(model, class)
				attr_keys = dict_keys(attrs)
				while (dict_len(attr_keys) > 0):
					attr_key = set_pop(attr_keys)
					elem = instantiate_node(model, "rendered/Text", "")
					instantiate_attribute(model, elem, "x", 5)
					instantiate_attribute(model, elem, "y", text_loc + 20)
					instantiate_attribute(model, elem, "lineWidth", 1)
					instantiate_attribute(model, elem, "lineColour", "black")
					instantiate_attribute(model, elem, "text", (attr_key + " = ") + cast_value(attrs[attr_key]))
					instantiate_attribute(model, elem, "layer", 2)
					instantiate_link(model, "rendered/contains", "", group, elem)
					text_loc = text_loc + 15

	// Flush all associations
	elements = allInstances(model, "rendered/ConnectingLine")
	while (set_len(elements) > 0):
		class = set_pop(elements)
		model_delete_element(model, class)

	// Rerender associations
	Element to_render
	to_render = set_create()
	class_types = allInstances(metamodel, "Association")
	while (set_len(class_types) > 0):
		class_type = set_pop(class_types)
		log("Checking type " + class_type)

		if (string_startswith(class_type, "abstract/")):
			elements = allInstances(model, class_type)
			log("    Checking instance " + class)
			while (set_len(elements) > 0):
				class = set_pop(elements)
				if (is_edge(model["model"][class])):
					if (bool_not(set_in(to_render, class))):
						set_add(to_render, class)
						log("Added!")

	to_render = set_to_list(to_render)
	Element delayed_elements
	Integer num_to_render
	delayed_elements = list_create()
	while (list_len(to_render) > 0):
		num_to_render = list_len(to_render)
		while (list_len(to_render) > 0):
			class = list_pop_final(to_render)
			attr_keys = dict_keys(getAttributes(model, class))

			if (bool_not(bool_and(dict_in(groups, readAssociationSource(model, class)), dict_in(groups, readAssociationDestination(model, class))))):
				list_append(delayed_elements, class)
				continue!

			elem = instantiate_link(model, "rendered/ConnectingLine", "", groups[readAssociationSource(model, class)], groups[readAssociationDestination(model, class)])
			dict_add(groups, class, elem)
			if (is_edge(model["model"][readAssociationSource(model, class)])):
				instantiate_attribute(model, elem, "offsetSourceX", 0)
				instantiate_attribute(model, elem, "offsetSourceY", 0)
			else:
				instantiate_attribute(model, elem, "offsetSourceX", 100)
				instantiate_attribute(model, elem, "offsetSourceY", 30)
			if (is_edge(model["model"][readAssociationDestination(model, class)])):
				instantiate_attribute(model, elem, "offsetTargetX", 0)
				instantiate_attribute(model, elem, "offsetTargetY", 0)
			else:
				instantiate_attribute(model, elem, "offsetTargetX", 100)
				instantiate_attribute(model, elem, "offsetTargetY", 30)
			instantiate_attribute(model, elem, "lineWidth", 3)
			instantiate_attribute(model, elem, "lineColour", "black")
			instantiate_attribute(model, elem, "arrow", True)
			instantiate_attribute(model, elem, "__asid", list_read(string_split_nr(class, "/", 1), 1))
			instantiate_attribute(model, elem, "layer", 0)
			instantiate_link(model, "rendered/contains", "", group, elem)

		if (num_to_render == list_len(delayed_elements)):
			log("Could not decrease number of rendered elements anymore... Giving up!")
			return True!
		else:
			to_render = delayed_elements
			delayed_elements = list_create()

	return True!
