include "primitives.alh"
include "modelling.alh"
include "object_operations.alh"
include "library.alh"
include "conformance_scd.alh"

Void function petrinet_enabled(model : Element):
	Element set_enabled
	set_enabled = petrinet_enabled_set(model)
	output("Enabled transitions:")
	while (0 < read_nr_out(set_enabled)):
		output(set_pop(set_enabled))
	return!

Element function petrinet_enabled_set(model : Element):
	Element all_transitions
	Element enabled_transitions
	String under_study
	Element in_arcs
	String arc_under_study
	Boolean enabled

	all_transitions = allInstances(model, "Transition")
	enabled_transitions = create_node()

	while (0 < read_nr_out(all_transitions)):
		under_study = set_pop(all_transitions)
		enabled = True

		// Find all incoming transitions
		in_arcs = allIncomingAssociationInstances(model, under_study, "P2T")

		while (0 < read_nr_out(in_arcs)):
			arc_under_study = set_pop(in_arcs)

			Integer present_tokens
			Integer required_tokens
			required_tokens = read_attribute(model, arc_under_study, "weight")
			log("Weight: " + cast_i2s(required_tokens))
			present_tokens = read_attribute(model, reverseKeyLookup(model["model"], read_edge_src(model["model"][arc_under_study])), "tokens")
			log("Tokens: " + cast_i2s(present_tokens))
			if (present_tokens < required_tokens):
				// Less tokens than required, so disable the transition completely
				enabled = False

		if (enabled):
			set_add(enabled_transitions, under_study)

	log("Got all enabled transitions!")
	return enabled_transitions!

Void function petrinet_fire(model : Element):
	output("Transition to fire?")
	String transition
	transition = input()
	if (dict_in(model["model"], transition)):
		if (set_in(petrinet_enabled_set(model), transition)):
			Element workset
			String working_place
			String working_arc
			Integer new_value

			// Consume tokens
			workset = allIncomingAssociationInstances(model, transition, "P2T")
			while (0 < read_nr_out(workset)):
				working_arc = set_pop(workset)
				working_place = reverseKeyLookup(model["model"], read_edge_src(model["model"][working_arc]))
				new_value = integer_subtraction(read_attribute(model, working_place, "tokens"), read_attribute(model, working_arc, "weight"))
				unset_attribute(model, working_place, "tokens")
				instantiate_attribute(model, working_place, "tokens", new_value)
				output((("  " + working_place) + ": ") + cast_i2s(read_attribute(model, working_place, "tokens")))

			// Add tokens
			workset = allOutgoingAssociationInstances(model, transition, "T2P")
			while (0 < read_nr_out(workset)):
				working_arc = set_pop(workset)
				working_place = reverseKeyLookup(model["model"], read_edge_dst(model["model"][working_arc]))
				new_value = integer_addition(read_attribute(model, working_place, "tokens"), read_attribute(model, working_arc, "weight"))
				unset_attribute(model, working_place, "tokens")
				instantiate_attribute(model, working_place, "tokens", new_value)
				output((("  " + working_place) + ": ") + cast_i2s(read_attribute(model, working_place, "tokens")))
			output("Transition fired!")
		else:
			output("Cannot fire if not enabled; aborting")
	else:
		output("Unknown transition; aborting")
	return!

Void function petrinet_list(model : Element):
	Element keys 
	Element typemap
	Element place
	String name

	keys = dict_keys(model["model"])
	typemap = model["type_mapping"]
	place = model["metamodel"]["model"]["Place"]

	while (list_len(keys) > 0):
		name = set_pop(keys)
		if (element_eq(dict_read_node(typemap, model["model"][name]), place)):
			// Found a place
			output((name + ": ") + cast_i2s(read_attribute(model, name, "tokens")))
	return!

Void function main():
	Element model
	String verify_result

	while (True):
		output("Which model do you want to execute with petri net semantics?")
		model = import_node(input())

		if (element_eq(model, read_root())):
			output("Could not find model; aborting")
		elif (element_neq(model["metamodel"], import_node("models/PetriNets"))):
			output("Not a PetriNets model; aborting")
		else:
			verify_result = conformance_scd(model)
			if (verify_result == "OK"):
				output("Model OK!")
				execute_petrinet(model)
			else:
				output("Non-conforming model: " + verify_result)

Void function execute_petrinet(model : Element):
	String cmd
	while (True):
		output("Which operation do you want to execute?")
		cmd = input()

		if (cmd == "help"):
			output("Supported operations:")
			output("  help    -- this information")
			output("  enabled -- list the enabled transitions")
			output("  fire    -- fire a transition")
			output("  list    -- list the state of the petrinet")
			output("  exit    -- select another model")
		elif (cmd == "enabled"):
			petrinet_enabled(model)
		elif (cmd == "fire"):
			petrinet_fire(model)
		elif (cmd == "list"):
			petrinet_list(model)
		elif (cmd == "exit"):
			return!
		else:
			output("Did not understand command!")
			output("Use 'help' for a list of available options")
