include "primitives.alh"
include "object_operations.alh"
include "typing.alh"

Boolean function find_type_mapping(model : Element):
	// Finds a correct type mapping for the provided model (with partial type mapping)
	// We go through several phases:
	//   1) remove elements from type mapping that are not in the model or metamodel
	//   2) find a mapping based on the current partial mapping
	//   3) (optional) verify that the mapping is correct with conformance checking
	// Returns True if the type mapping was altered

	// Start of with some initialization
	Element tm
	Element elems
	String elem

	tm = get_type_mapping_as_dict(model)

	// 1) remove elements from type mapping that are not in the model or metamodel
	elems = dict_keys(tm)
	while (set_len(elems) > 0):
		elem = set_pop(elems)
		if (bool_not(dict_in(model["model"], elem))):
			// Remove the key, as the model does not contain the element anymore
			dict_delete(tm, elem)
		else:
			if (bool_not(dict_in(model["metamodel"]["model"], tm[elem]))):
				// Remove the key, as the metamodel does not contain the type anymore
				dict_delete(tm, elem)
		
	// 2) find a mapping based on the current partial mapping, but only if it is not yet complete
	// TODO this must be expanded for other things than trivial metamodels!
	if (dict_len(model["model"]) > dict_len(tm)):
		log("Model is incompletely typed!")
		log("Model has: " + set_to_string(dict_keys(model["model"])))
		log("Type mapping has: " + set_to_string(dict_keys(tm)))
		log("Difference: " + set_to_string(set_difference(dict_keys(model["model"]), dict_keys(tm))))

		// TODO for now, this only returns something for a simple case, where the MM has one edge, and one node
		//      and it makes the assumption that SCD is the M3 level...

		// First find the name of the edge and node elements
		Element elems
		String elem

		String node_element
		String edge_element
		node_element = read_root()
		edge_element = read_root()

		Element nodes
		Element edges
		nodes = allInstances(model["metamodel"], "Class")
		edges = allInstances(model["metamodel"], "Association")

		if (set_len(nodes) > 1):
			log("Got nodes: " + set_to_string(nodes))
			log("Multiple nodes detected!")
			return False!
		elif (set_len(edges) > 1):
			log("Got edges: " + set_to_string(edges))
			log("Multiple edges detected!")
			return False!
		elif (set_len(nodes) != 1):
			log("No node found!")
			return False!
		elif (set_len(edges) != 1):
			log("No edge found!")
			return False!
		else:
			node_element = set_pop(nodes)
			edge_element = set_pop(edges)
			log("Found node: " + node_element)
			log("Found edge: " + edge_element)

		// Now we have bot an edge_element and node_element of the metamodel
		// Now just trivially bind all elements!
		elems = dict_keys(model["model"])
		while (set_len(elems) > 0):
			elem = set_pop(elems)

			if (is_edge(model["model"][elem])):
				retype(model, elem, edge_element)
			else:
				retype(model, elem, node_element)

	// 3) (optional) verify that the mapping is correct with conformance checking
	// TODO
	
	return True!
