"""The template for a "write a program" question type that accepts answers
    in C, C++, Java or Python3 according to the value in the language select
    dropdown menu.
"""

import subprocess
import re
student_answer = """{{ STUDENT_ANSWER | e('py') }}"""
language = """{{ ANSWER_LANGUAGE | e('py') }}""".lower()
language_extension_map = {'c':'c', 'cpp':'cpp', 'java':'java', 'python3':'py'}

if language not in language_extension_map.keys():
    raise Exception('Error in question. Unknown/unexpected language ({})'.format(language))

if language == 'java':
    # Need to determine public class name in order to name output file. Sigh.
    # The best I can be bothered to do is to use a regular expression match.
    match = re.search(r'public\s+class\s+([_a-zA-Z][_a-zA-Z0-9]*)', student_answer, re.DOTALL | re.MULTILINE)
    if match is None:
        raise Exception("Unable to determine class name. Does the file include 'public class name'?")
    classname = match.group(1)
    filename = classname + '.java'
else:
    filename = '__tester__.' + language_extension_map[language]

# Write the student code to a file

with open(filename, "w") as src:
    print(student_answer, file=src)

# Compile C, C++ and Java
if language == 'c':
    cflags = "-std=c99 -Wall -Werror"
    return_code = subprocess.call("gcc {0} -o __tester__ __tester__.c".format(cflags).split())
    if return_code != 0:
        raise Exception("** Compilation failed. Testing aborted **")
    exec_command = ["./__tester__"]
elif language == 'cpp':
    cppflags = "-Wall -Werror"
    return_code = subprocess.call("g++ {0} -o __tester__ __tester__.cpp".format(cppflags).split())
    if return_code != 0:
        raise Exception("** Compilation failed. Testing aborted **")
    exec_command = ["./__tester__"]
elif language == 'java':
    return_code = subprocess.call(['javac', "-J-Xss64m", "-J-Xmx4g", filename])
    if return_code != 0:
        raise Exception("** Compilation failed. Testing aborted **")
    exec_command = ["java", "-Xss16m", "-Xmx500m", classname]
else: # Python doesn't need a compile phase
    exec_command = ["python3", "./__tester__.py"]

# Now run the code. Since this is a per-test template,
# stdin is already set up for the stdin text specified in the test case,
# so we can run the compiled program directly.

try:
    output = subprocess.check_output(exec_command, universal_newlines=True)
    print(output)
except subprocess.CalledProcessError as e:
    if e.returncode > 0:
        # Ignore non-zero positive return codes
        if e.output:
            print(e.output)
    else:
        # But negative return codes are signals - abort
        if e.output:
            print(e.output, file=sys.stderr)
        if e.returncode < 0:
            print("Task failed with signal", -e.returncode, file=sys.stderr)
        print("** Further testing aborted **", file=sys.stderr)