include "primitives.alh"
include "utils.alh"

Element function get_type_mapping(model : Element):
	// Deserialize dictionary as model
	Element tm_model
	tm_model = dict_create()

	Element m
	Element mm
	Element edge
	Element keys
	String key

	keys = dict_keys(model["type_mapping"])

	while (set_len(keys) > 0):
		key = set_pop(keys)
		m = model["model"][key]
		mm = model["metamodel"]["model"][model["type_mapping"][key]]
		edge = create_edge(m, mm)

		dict_add_fast(tm_model, cast_id(m), m)
		if (bool_not(dict_in(tm_model, cast_id(mm)))):
			dict_add_fast(tm_model, cast_id(mm), mm)
		dict_add_fast(tm_model, cast_id(edge), edge)

	return tm_model!

Void function set_type_mapping(model : Element, type_mapping_model : Element):
	// Serialize model to dictionary
	Element type_mapping
	Element rev_model
	Element rev_metamodel
	Element keys
	String key

	type_mapping = dict_create()
	keys = dict_keys(type_mapping_model)
	rev_model = make_reverse_dictionary(model["model"])
	rev_metamodel = make_reverse_dictionary(model["metamodel"]["model"])

	while (set_len(keys) > 0):
		key = set_pop(keys)
		if (is_edge(type_mapping_model[key])):
			if (bool_not(bool_or(dict_in(rev_model, cast_id(type_mapping_model[key])), dict_in(rev_metamodel, cast_id(type_mapping_model[key]))))):
				// Element is in neither model or metamodel
				// Must be a typing link!
				// So add it
				dict_add_fast(type_mapping, rev_model[cast_id(read_edge_src(type_mapping_model[key]))], rev_metamodel[cast_id(read_edge_dst(type_mapping_model[key]))])

	dict_overwrite(model, "type_mapping", type_mapping)
	return!

Element function elements_typed_by(model : Element, type_name : String):
	Element result
	Element temp_result
	String temp

	result = set_create()
	temp_result = reverseKeyLookupMulti(get_type_mapping_as_dict(model), type_name)
	while (set_len(temp_result) > 0):
		temp = set_pop(temp_result)
		if (dict_in(model["model"], temp)):
			set_add(result, temp)
	return result!

Element function get_type_mapping_as_dict(model : Element):
	return model["type_mapping"]!

String function read_type(model : Element, name : String):
	String result
	Element tm
	if (dict_in(model["model"], name)):
		if (dict_in(model["type_mapping"], name)):
			result = model["type_mapping"][name]
			if (dict_in(model["metamodel"]["model"], result)):
				return result!
			else:
				return ""!
		else:
			return ""!
	else:
		return ""!

Void function retype(model : Element, element : String, type : String):
	// Retype a model, deleting any previous type the element had
	// The type string is evaluated in the metamodel previously specified
	dict_overwrite(model["type_mapping"], element, type)
	return!

Void function new_type_mapping(model : Element):
	dict_overwrite(model, "type_mapping", dict_create())
	return !

Void function remove_type(model : Element, name : String):
	if (dict_in(model["type_mapping"], name)):
		dict_delete(model["type_mapping"], name)
	
	String elem
	elem = cast_id(model["model"][name])
	if (dict_in(model["type_mapping"], elem)):
		dict_delete(model["type_mapping"], elem)

	return !
