include "primitives.alh"
include "conformance_scd.alh"
include "constructors.alh"

Element function allInstances(model : Element, type_name : String):
	Element type_mapping
	Element result
	Element type

	String key
	Element keys

	keys = dict_keys(model["model"])
	type = model["metamodel"]["model"][type_name]
	result = create_node()

	while (0 < list_len(keys)):
		key = set_pop(keys)
		if (is_nominal_instance(model, key, type_name)):
			set_add(result, key)

	return result

Element function selectPossibleIncoming(model : Element, target : String, limit_set : Element):
	// Find all possible incoming link types for the target model
	// Should also include those specified on the superclass(es)

	String type
	Element model_dict
	Element elem
	Element result
	Element target_element

	result = create_node()
	model_dict = model["model"]

	while (0 < list_len(limit_set)):
		type = set_pop(limit_set)
		elem = model_dict[type]
		if (is_nominal_subtype(model, target, reverseKeyLookup(model_dict, read_edge_dst(elem)))):
			set_add(result, type)
	
	return result

Element function selectPossibleOutgoing(model : Element, source : String, limit_set : Element):
	// Find all possible outgoing link types for the source model
	// Should also include those specified on the superclass(es)
	String type
	Element model_dict
	Element elem
	Element result
	Element source_element

	result = create_node()
	model_dict = model["model"]
	
	while (0 < list_len(limit_set)):
		type = set_pop(limit_set)
		elem = model_dict[type]
		if (is_nominal_subtype(model, source, reverseKeyLookup(model_dict, read_edge_src(elem)))):
			set_add(result, type)
	
	return result

Element function allOutgoingAssociationInstances(model : Element, source_name : String, assoc_name : String):
	// Read out all outgoing edges of the model and select those that are typed by the specified association
	Element assocs
	String assoc
	Element result
	Element source

	assocs = allInstances(model, assoc_name)
	source = model["model"][source_name]

	result = create_node()
	while (0 < list_len(assocs)):
		assoc = set_pop(assocs)
		if (element_eq(source, read_edge_src(model["model"][assoc]))):
			set_add(result, assoc)
	return result

Element function allIncomingAssociationInstances(model : Element, target_name : String, assoc_name : String):
	// Read out all outgoing edges of the model and select those that are typed by the specified association
	Element assocs
	String assoc
	Element result
	Element target

	assocs = allInstances(model, assoc_name)
	target = model["model"][target_name]

	result = create_node()
	while (0 < list_len(assocs)):
		assoc = set_pop(assocs)
		if (element_eq(target, read_edge_dst(model["model"][assoc]))):
			set_add(result, assoc)
	return result

Element function getAttributeList(model : Element, element : String):
	Element result
	Element keys
	Element type
	Element attr_name
	String attr_type

	result = create_node()
	type = dict_read_node(model["type_mapping"], model["model"][element])
	keys = dict_keys(type)

	// Add our own attributes
	while (0 < list_len(keys)):
		attr_name = set_pop(keys)
		if (is_physical_string(attr_name)):
			attr_type = reverseKeyLookup(model["metamodel"]["model"], type[attr_name])
			dict_add(result, attr_name, attr_type)

	// Go on to the metalevel
	// TODO

	return result

Element function getInstantiatableAttributes(model : Element, element : String):
	Element result
	result = create_node()
	// Get all outgoing "dictionary" links
	Element set_own
	Element elem
	elem = model["model"][element]
	set_own = dict_keys(element)

	// Filter them
	Element e
	while (0 < read_nr_out(set_own)):
		e = set_pop(set_own)
		if (is_physical_string(e)):
			dict_add(result, e, reverseKeyLookup(model["model"], element[e]))

	return result

String function reverseKeyLookup(dict : Element, element : Element):
	Element elements
	String name

	elements = dict_keys(dict)
	while (0 < list_len(elements)):
		name = set_pop(elements)
		if (element_eq(dict[name], element)):
			return name

	return string_join(string_join("(unknown: ", cast_e2s(element)), " )")

String function print_dict(dict : Element):
	Element keys
	Element key
	String result

	keys = dict_keys(dict)
	result = ""
	while (0 < list_len(keys)):
		key = set_pop(keys)
		result = result + cast_v2s(key)
		result = result + ": "
		result = result + cast_v2s(dict[key])
		result = result + "\n"
	return result

String function readAssociationSource(model : Element, name : String):
	return reverseKeyLookup(model["model"], read_edge_src(model["model"][name]))

String function readAssociationDestination(model : Element, name : String):
	return reverseKeyLookup(model["model"], read_edge_dst(model["model"][name]))

String function followAssociation(model : Element, element_name : String, association_name : String):
	Element assocs
	String assoc
	Element result

	assocs = allOutgoingAssociationInstances(model, element_name, association_name)
	result = create_node()
	while (0 < list_len(assocs)):
		set_add(result, readAssociationDestination(model, set_pop(assocs)))

	return result
