#!/usr/bin/env python

## Version 2.0
## Written by Peter Sinclair, based on code by Ting Chan

## Previous Versions:
## 1.0	Original script
## 1.1	Documentation added
## 1.2	Updated to accommodate both Scheme and Python
## 1.3	Updated to accommodate any course number
## 2.0	Added "simple" mode (and named previous mode "standard")

## SAMPLE INPUT for STANDARD MODE (all of the following formats will work):
##
## Single line value:
##
## value = 1
## value = {1}
## value = "1"
##
## Multi-line values:
##
## output = {One Line}
## output =
## {More than
## one line}
## output = {Also more
## than one
## line}
##
## Multi-line value with filename:
##
## file = filename
## {Content
## of
## file}
## file = {filename}
## {Content of File}

import os
import sys
import shutil

if len(sys.argv)!=4:
	print "Usage:", sys.argv[0], "assignment, question #, file"
	sys.exit(1)
else:
	asst_num = sys.argv[1]
	question = sys.argv[2]
	filename = sys.argv[3]

## Checks for course number in script path; if not present, prompts the user.
course = ""
for c in ["cs115", "cs116", "cs135", "cs136"]:
	if c in sys.argv[0]:
		course = c
if course == "":
	course = raw_input("Please enter the course code (ex: cs116): ")

## Tries to open filename; if filename does not exist in course/marking/asst_num/test.1
##    then the script prints an error message and terminates.
try:
	infile=file('/u4/%s/marking/%s/test.1/%s' % (course, asst_num, filename),'r')
	lines = infile.readlines()
	infile.close()
except:
	print '/u4/%s/marking/%s/test.1/%s file doesn\'t exist; no files created.' % (course, asst_num, filename)
	sys.exit(1)

## Variables to navigate lines:
##   n is the current position in lines
##   line is reset to lines[n] each time n changes, but is often reduced
##     to only the important part of the line while processing.
n=0
while lines[n] == "\n":
	n += 1
line = lines[n]

## Function to handle multi-line values
def multi_line_value():
	global line
	global n
	temp = ""
	if '{' in line and '}' in line:
		temp += line[line.index('{')+1:line.index('}')]
	else:
		while '{' not in line:
			n += 1
			line = lines[n]
		temp += line[line.index('{')+1:]
		n += 1
		line = lines[n]
		while '}' not in line:
			temp += line
			n += 1
			line = lines[n]
		temp += line[:line.index('}')]
#		if temp[0] == "\n": temp=temp[1:]       # Uncomment these lines if you want to remove a
#		if temp[-1] == "\n": temp=temp[:-1]     # single newline from the start/end of temp
	return temp

## Initialize variables for /in/question/options.rkt
options, value, desc, language, loadcode, mode = '', '', '', '', '', ''

## Loop to collect information /in/question/options.rkt as follows:
##    If line[0] does not equal 'q', the loop terminates
##    Otherwise, options, value, loadcode, or desc will update to equal
##       the remainder of the line.
while line[0] == 'q':
	if line.startswith('q_options'):
		line = line[7:]
		options += multi_line_value()
	elif line.startswith('q_value'):
		line = line[7:].strip('= \n\r{}\'\"')
		value = '(value %s)\n' % line
	elif line.startswith('q_loadcode'):
		line = line[10:].strip('= \n\r{}\'\"')
		loadcode = '(loadcode "%s")\n' % line
	elif line.startswith('q_desc'):
		line = line[6:].strip('= \n\r{}\'\"')
		desc = '(desc "%s")\n' % line
	elif line.startswith('q_language'):
		line = line[10:].strip('= \n\r{}\'\"')
		language = '(language %s)' % line
	elif line.startswith('q_mode'):
		mode = line[6:].strip('= \n\r{}\'\"')
	n += 1
	line = lines[n]

## Checks that a language has been specified either in filename or test.1/in/options.rkt;
##   If it hasn't, prints an error message and aborts
##   If it has, sets extension to either .rkt or .py depending on the language specified
extension = ""
if language == "":
	F = file('/u4/%s/marking/%s/test.1/in/options.rkt' % (course, asst_num))
	big_options = F.read()
	F.close()
	if "scheme" in big_options:   extension = "rkt"
	elif "python" in big_options: extension = "py"
else:
	if "scheme" in language:   extension = "rkt"
	elif "python" in language: extension = "py"
if extension == "":
	print 'No language specified in test.1/%s or in test.1/in/options.rkt; no files created' % filename
	sys.exit(1)

## Prompts the user for an appropriate mode until a recognised mode is entered.
while mode not in ["simple", "standard", ""]:
	mode = raw_input("Mode not recognised. Please enter either simple or standard: ")

## Deletes test.1/in/question (if it exists) and creates a new, more empty test.1/in/question
try:	shutil.rmtree('/u4/%s/marking/%s/test.1/in/%s' % (course, asst_num, question))
except:	None
os.mkdir('/u4/%s/marking/%s/test.1/in/%s' % (course, asst_num, question))
os.chdir('/u4/%s/marking/%s/test.1/in/%s' % (course, asst_num, question))

## Writes desc, loadcode, value, and options to /in/question/options.rkt
## loadcode is set to aXqY.rkt or aXqY.py if it was not set in filename
if loadcode == "":
	loadcode = '(loadcode "%sq%s.%s")\n' % (asst_num, question, extension)
optionsfile = file('options.rkt', 'w')
optionsfile.writelines([desc, loadcode, value, options])
optionsfile.close()

## Skip any blank lines between question information and tests
while lines[n] == "\n":
	n += 1

test_num = 1 ## The current test
## name_test_dir produces an appropriate name for the current testing directory
##    based on test_num:
##    * If test_num is a one-digit number, then produces t0X, where X is test_num
##    * If test_num is longer than one digit, then produces tX, where X is test_num
def name_test_dir():
	if test_num < 10:
		return 't0%d' % test_num
	else:
		return 't%d' % test_num
test_dir = name_test_dir()

## Variables used in write_test() and following loop
value, desc, options, test_text, input, output = '', '', '', '', '', ''
files, duplicate_files = {}, []

## Function for writing necessary test files (if a field is empty, it is not written):
##    IMPORTANT: This function begins by deleting /in/asst_num and all subdirectories
##               and creating a new, empty /in/asst_num
##    test_text will be written to /in/question/test_dir/test.py (or test.rkt)
##    desc, value, and options will be written to /in/question/test_dir/options.rkt
##    input will be written to /in/question/test_dir/input
##    output will be written to /answers/question_test_dir.out
##    The value of each filename in files will be written to /provided/filename
## The function also displays what files are written for the test
def write_test():
	os.mkdir('/u4/%s/marking/%s/test.1/in/%s/%s' % (course, asst_num, question, test_dir))
	os.chdir('/u4/%s/marking/%s/test.1/in/%s/%s' % (course, asst_num, question, test_dir))
	test_file = file('test.%s' % extension, 'w')
	test_file.write(test_text)
	test_file.close()
	files_created = ['test.%s' % extension]
	
	if options or value or desc:
		F = file('options.rkt', 'w')
		F.writelines([desc, value, options])
		F.close
		files_created.append("options.rkt")
	if input:
		F = file('input', 'w')
		F.write(input)
		F.close
		files_created.append('input')
	if output:
		os.chdir('/u4/%s/marking/%s/test.1/answers' % (course, asst_num))
		F = file('%s_%s.out' % (question, test_dir), 'w')
		F.write(output)
		F.close
		files_created.append('%s_%s.out' % (question, test_dir))
	if files:
		os.chdir('/u4/%s/marking/%s/test.1/provided' % (course, asst_num))
		for filename in files:
			F = file(filename, 'w')
			F.write(files[filename])
			F.close
			files_created.append(filename)
	
	## English is a silly language, so it takes this much code to display
	##    which files were created in a correctly punctuated fashion
	print_message = test_dir + ": Created "
	if len(files_created) == 1:
		print_message += files_created[0]
		files_created = []
	elif len(files_created) == 2:
		print_message += files_created[0] + ' and ' + files_created[1]
		files_created = []
	while len(files_created) > 2:
		print_message += files_created[0] + ', '
		files_created = files_created[1:]
	if len(files_created) == 2:
		print_message += files_created[0] + ', and ' + files_created[1]
	print print_message
	for name in sorted(duplicate_files):
		print "Attempted to create multiple files named %s; only the first was created" % name

## Loop to take data in from lines and call write_test() as follows:
##    If line == '\n' then the information for that test is complete, and the
##       loop will call write_test() before resetting all variables
##    If line begins with value, desc, options, input, output, or file,
##       then the appropriate field will be updated.
##    Otherwise, the information will be stored in test_text
##       (which will be written verbatim to test.py or test.rkt)
##    The loop will finish with the final test unwritten; the if statement
##       following the loop writes the final test.
mode = "simple"
while n < len(lines):
	line = lines[n]
	if line == '\n':
		try:	write_test()
		except:	print "Error writing", test_dir
		test_num += 1
		test_dir = name_test_dir()
		value, desc, options, test_text, input, output = '', '', '', '', '', ''
		files, duplicate_files = {}, []
	elif line.startswith('value'):
		line = line[5:].strip('= \n\r{}\'\"')
		value = '(value %s)\n' % line
	elif line.startswith('desc'):
		line = line[4:].strip('= \n\r{}\'\"')
		desc = '(desc "%s")\n' % line
	elif line.startswith('options'):
		line = line[7:]
		options += multi_line_value()
	elif line.startswith('input'):
		line = line[5:]
		input += multi_line_value()
	elif line.startswith('output'):
		line = line[6:]
		output += multi_line_value()
	elif line.startswith('file'):
		line = line[4:].strip('= \n\r{}\'\"')
		name = line
		n += 1
		line = lines[n]
		body = ""
		body += multi_line_value()
		if files.has_key(name):
			duplicate_files.append(name)
		else:
			files[name] = body
	elif mode == "simple" and extension == "py":
		test_text += "result = " + line
		n += 1
		line = lines[n]
		test_text += "expected = " + line
	elif mode == "simple" and extension == "rkt":
		line = line.strip()
		test_text += "(result " + line + ")\n"
		n += 1
		line = lines[n].strip()
		test_text += "(expected " + line + ")\n"
	else:
		test_text += line
	n += 1

## This if statement writes the final test	
if test_text != '':
	try:	write_test()
	except:	print "Error writing", test_dir