include "primitives.alh"
include "modelling.alh"
include "object_operations.alh"

String function get_indent(indentation : Integer):
	String code
	code = ""
	while (indentation > 0):
		code = code + "    "
		indentation = indentation - 1
	return code!

String function code_print(node : Element, indentation : Integer):
	String code
	String inst_type

	inst_type = cast_value(node)
	code = "(no_printer_for_" + inst_type + ")"

	if (inst_type == "if"):
		String cond_code
		String true_code
		String false_code

		cond_code = code_print(node["cond"], 0)
		true_code = code_print(node["then"], indentation + 1)
		if (dict_in(node, "else")):
			false_code = get_indent(indentation) + "else:\n"
			false_code = code_print(node["else"], indentation + 1)
		else:
			false_code = ""

		code = get_indent(indentation) + "if(" + cond_code + "):\n" + true_code + false_code
	elif (inst_type == "constant"):
		String value
		value = cast_value(node["node"])
		if (value == "true"):
			value = "True"
		elif (value == "false"):
			value = "False"
		code = get_indent(indentation) + value
	elif (inst_type == "return"):
		if (dict_in(node, "value")):
			code = get_indent(indentation) + "return " + code_print(node["value"], 0) + "\n"
		else:
			code = get_indent(indentation) + "return\n"
	elif (inst_type == "declare"):
		code = ""
	elif (inst_type == "resolve"):
		if (is_physical_string(node["var"])):
			code = cast_string(node["var"])
		else:
			code = "var_" + cast_id(node["var"])
	elif (inst_type == "assign"):
		code = get_indent(indentation) + code_print(node["var"], 0) + " = " + code_print(node["value"], 0) + "\n"
	elif (inst_type == "access"):
		code = code_print(node["var"], indentation)
	elif (inst_type == "while"):
		code = get_indent(indentation) + "while (" + code_print(node["cond"], 0) + "):\n" + code_print(node["body"], indentation + 1)
	elif (inst_type == "call"):
		String func_name
		String params

		params = ""
		func_name = code_print(node["func"], 0)

		Element param
		param = node["params"]
		Boolean continue
		continue = True
		while (continue):
			if (params == ""):
				params = code_print(param["value"], 0)
			else:
				params = params + ", " + code_print(param["value"], 0)
			if (bool_not(dict_in(param, "next_param"))):
				continue = False
			param = param["next_param"]

		code = func_name + "(" + params + ")"
		if (indentation > 0):
			code = get_indent(indentation) + code + "\n"

	if (dict_in(node, "next")):
		code = code + code_print(node["next"], indentation)

	return code!

Boolean function main(model : Element):
	// Read out the main function
	log("Start up MAIN function")
	String al_node
	Element function_element
	String code

	Element funcdefs
	String function
	funcdefs = allInstances(model, "AL/funcdef")
	code = ""
	while (set_len(funcdefs) > 0):
		// Iterate over all functions
		function = set_pop(funcdefs)

		log("Starting at function " + function)
		log("Got keys: " + set_to_string(dict_keys(model["model"][function])))
		log("Parameters:")
		Element params
		String param
		params = dict_keys(model["model"][function]["params"])
		code = code + "def func_" + cast_id(model["model"][function]) + "("
		while (set_len(params) > 0):
			param = set_pop(params)
			log("  " + param + " --> " + cast_id(model["model"][function]["params"][param]))
			code = code + "var_" + cast_id(model["model"][function]["params"][param])
			if (set_len(params) > 0):
				code = code + ", "
		code = code + "):\n"
			
		function_element = model["model"][function]["body"]
		al_node = cast_value(function_element)
		while (cast_value(function_element) == "global"):
			log("Assigning to " + cast_value(function_element["next"]["var"]["var"]))
			log("  the element: " + cast_id(function_element["next"]["value"]["node"]))
			log("  keys: " + set_to_string(dict_keys(function_element["next"]["value"]["node"])))
			log("Function is element: " + cast_id(function_element["next"]["value"]["node"]))
			function_element = function_element["next"]["next"]
		log("Started execution at " + cast_value(function_element))
		log("Started REAL execution at " + cast_value(function_element))
		
		code = code + code_print(function_element, 1)

	log("Finding initial function...")
	function = set_pop(allInstances(model, "AL/Initial"))
	function = set_pop(allAssociationDestinations(model, function, "AL/initial_funcdef"))
	log("Initial function: " + cast_id(model["model"][function]))
	code = code + "main = func_" + cast_id(model["model"][function]) + "\n"

	// Found the main function, though we only have the name of the function to assign...
	// What we can do is reassign main to that function!

	log("Generated code:")
	log(code)

	log("Function block can now be invoked by calling the 'main' function!")
	return True!
