include "primitives.alh"
include "constructors.alh"
include "object_operations.alh"
include "library.alh"
include "conformance_scd.alh"
include "io.alh"
include "metamodels.alh"
include "modelling.alh"
include "compilation_manager.alh"

Element function modify(model : Element):
	String cmd

	Element attr_list_pn
	Element attr_keys_pn
	String attr_key_pn
	Element metamodel_element_pn
	String typename

	output("Model loaded, ready for commands!")
	output("Use 'help' command for a list of possible commands")

	while (True):
		output("Please give your command.")
		cmd = input()
		if (cmd == "help"):
			output("Generic model operations:")
			output("  instantiate -- Create a new model element")
			output("  delete      -- Delete an existing element")
			output("  attr_add    -- Add an attribute to an element")
			output("  attr_del    -- Delete an attribute of an element")
			output("  constrain   -- Add a constraint function to the model")
			output("  rename      -- Rename an existing element")
			output("  modify      -- Modify the attributes of an element")
			output("  list        -- Prints the list of elements in the model")
			output("  types       -- Prints the list of elements that can be instantiated")
			output("  read        -- Prints the current state of a model element")
			output("  verify      -- Check whether the model conforms to the metamodel")
			output("  retype      -- Change the type of an element")
			output("  exit        -- Leave the modification interface")
		elif (cmd == "exit"):
			return model!
		elif (cmd == "instantiate"):
			String mm_type_name
			output("Type to instantiate?")
			mm_type_name = input()
			if (dict_in(model["metamodel"]["model"], mm_type_name)):
				String element_name
				output("Name of new element?")
				element_name = input()
				if (dict_in(model["model"], element_name)):
					output("Element already exists; aborting")
				else:
					if (is_edge(model["metamodel"]["model"][mm_type_name])):
						output("Source name?")
						String src_name
						src_name = input()
						if (dict_in(model["model"], src_name)):
							output("Destination name?")
							String dst_name
							dst_name = input()
							if (dict_in(model["model"], dst_name)):
								instantiate_link(model, mm_type_name, element_name, src_name, dst_name)
								output("Instantiation successful!")
							else:
								output("Unknown destination; aborting")
						else:
							output("Unknown source; aborting")
					else:
						instantiate_node(model, mm_type_name, element_name)
						output("Instantiation successful!")
			else:
				output("Unknown type specified; aborting")
		elif (cmd == "set_inheritance"):
			String inh_name

			output("Which link in the metamodel is the inheritance link?")
			inh_name = input()

			if (dict_in(model["metamodel"]["model"], inh_name)):
				dict_add(model, "inheritance", model["metamodel"]["model"][inh_name])
				output("Set inheritance link!")
			else:
				output("Element not found in metamodel; aborting")

		elif (cmd == "constrain"):
			output("Element to constrain (empty for global)?")
			String model_name
			model_name = input()

			if (model_name == ""):
				// Global constraint
				output("Give input to function constructors for GLOBAL constraint!")
				set_model_constraints(model, construct_function())
			elif (dict_in(model["model"], model_name)):
				// Local constraint for this model
				output("Give input to function constructors for LOCAL constraint!")
				add_constraint(model, model_name, construct_function())
				output("Added constraint to model!")
			else:
				// Local constraint, but model not found
				output("Unknown model; aborting")
		elif (cmd == "modify"):
			String model_name
			output("Element to modify?")
			model_name = input()
			if (dict_in(model["model"], model_name)):
				Element attrs
				attrs = getAttributeList(model, model_name)
				String attr_name
				output("Attribute to modify?")
				attr_name = input()
				if (set_in(dict_keys(attrs), attr_name)):
					output("New value?")
					unset_attribute(model, model_name, attr_name)
					instantiate_attribute(model, model_name, attr_name, input())
					output("Modified!")
				else:
					output("No such attribute!")
			else:
				output("No such model!")
		elif (cmd == "attr_add"):
			String model_name
			output("Which model do you want to assign an attribute to?")
			model_name = input()
			if (dict_in(model["model"], model_name)):
				Element attrs
				attrs = getAttributeList(model, model_name)
				String attr_name
				output("Which attribute do you wish to assign?")
				attr_name = input()
				if (set_in(dict_keys(attrs), attr_name)):
					output("Value of attribute?")
					instantiate_attribute(model, model_name, attr_name, input())
					output("Added attribute!")
				else:
					output("No such attribute!")
			else:
				output("No such model!")
		elif (cmd == "attr_del"):
			String model_name
			output("Which model do you want to remove an attribute of?")
			model_name = input()
			if (dict_in(model["model"], model_name)):
				Element attrs
				attrs = getAttributeList(model, model_name)
				String attr_name
				output("Which attribute do you want to delete?")
				attr_name = input()
				if (set_in(dict_keys(attrs), attr_name)):
					unset_attribute(model, model_name, attr_name)
					output("Attribute deleted!")
				else:
					output("No such attribute!")
			else:
				output("No such model!")
		elif (cmd == "delete"):
			output("What is the name of the element you want to delete?")
			cmd = input()
			if (dict_in(model["model"], cmd)):
				model_delete_element(model, cmd)
				output("Deleted!")
			else:
				output("No such element; aborting")
		elif (cmd == "rename"):
			output("Old name?")
			String old_name_e
			old_name_e = input()
			if (dict_in(model["model"], old_name_e)):
				output("New name?")
				String new_name_e
				new_name_e = input()
				if (dict_in(model["model"], new_name_e)):
					output("New name already used; aborting")
				else:
					dict_add(model["model"], new_name_e, model["model"][old_name_e])
					dict_delete(model["model"], old_name_e)
					output("Rename complete!")
			else:
				output("Unknown element; aborting")
		elif (cmd == "list"):
			Element keys_m
			keys_m = dict_keys(model["model"])
			output("List of all elements:")
			String v_m
			while (read_nr_out(keys_m) > 0):
				v_m = set_pop(keys_m)
				// Filter out anonymous objects
				if (bool_not(string_startswith(v_m, "__"))):
					typename = reverseKeyLookup(model["metamodel"]["model"], dict_read_node(model["type_mapping"], model["model"][v_m]))
					output((("  " + v_m) + " : ") + typename)
		elif (cmd == "read"):
			output("Element to read?")
			cmd = input()
			if (dict_in(model["model"], cmd)):
				Element read_elem
				read_elem = model["model"][cmd]
				metamodel_element_pn = dict_read_node(model["type_mapping"], read_elem)

				output("Name: " + cmd)
				output("Type: " + reverseKeyLookup(model["metamodel"]["model"], metamodel_element_pn))
				if (is_edge(read_elem)):
					output("Source: " + reverseKeyLookup(model["model"], read_edge_src(read_elem)))
					output("Destination: " + reverseKeyLookup(model["model"], read_edge_dst(read_elem)))
				if (cast_v2s(read_elem) != "None"):
					output("Value: " + cast_v2s(read_elem))
				output("Defines attributes:")
				attr_list_pn = getInstantiatableAttributes(model, read_elem)
				attr_keys_pn = dict_keys(attr_list_pn)
				while (0 < read_nr_out(attr_keys_pn)):
					attr_key_pn = set_pop(attr_keys_pn)
					output(((("  " + attr_key_pn) + " : ") + cast_v2s(attr_list_pn[attr_key_pn])))
				output("Attributes:")
				attr_list_pn = getAttributeList(model, cmd)
				attr_keys_pn = dict_keys(attr_list_pn)
				while (0 < read_nr_out(attr_keys_pn)):
					attr_key_pn = set_pop(attr_keys_pn)
					output((((("  " + cast_v2s(attr_key_pn)) + " : ") + cast_v2s(attr_list_pn[attr_key_pn])) + " = ") + cast_v2s(read_attribute(model, reverseKeyLookup(model["model"], read_elem), attr_key_pn)))
			else:
				output("Unknown element; aborting")
		elif (cmd == "verify"):
			output(conformance_scd(model))
		elif (cmd == "types"):
			Element keys_t
			keys_t = dict_keys(model["metamodel"]["model"])
			output("List of types:")
			String v_t
			while (read_nr_out(keys_t) > 0):
				v_t = set_pop(keys_t)
				if (bool_not(string_startswith(v_t, "__"))):
					output(string_join(("  " + v_t) + " : ", reverseKeyLookup(model["metamodel"]["metamodel"]["model"], dict_read_node(model["metamodel"]["type_mapping"], model["metamodel"]["model"][v_t]))))
		elif (cmd == "retype"):
			output("Element to retype?")
			String elementname
			elementname = input()
			if (dict_in(model["model"], elementname)):
				output("New type")
				typename = input()
				if (dict_in(model["metamodel"]["model"], typename)):
					// OK, do the retyping
					// First try removing the previous type if it exists
					dict_delete_node(model["type_mapping"], model["model"][elementname])
					// Now add the new type
					dict_add(model["type_mapping"], model["model"][elementname], model["metamodel"]["model"][typename])
					output("Retyped!")
				else:
					output("Unknown type; aborting")
			else:
				output("Unknown element; aborting")
		else:
			output("Unknown command: " + cast_v2s(cmd))
			output("Use command 'help' to get a list of available commands")
