#!/usr/bin/env python3
#
# Testing tool for the Ducky Debugging problem
#
# Usage:
#
#   python3 testing_tool.py -f inputfile <program invocation>
#
# Use the -f parameter to specify the input file.
# The lines of the file will be used as input to your submissions.
# The input file should only contain lines ending in a period, question mark, or exclamation mark.
#
# For python, you could invoke this tool using
#
#   python3 testing_tool.py -f 1.in python3 my_submission.py
#
# For C++, you should first compile your code, and assuming this gives the binary a.out, use
#
#   python3 testing_tool.py -f 1.in ./a.out
#
# If you have a Java solution that you would run using
# "java MyClass", you could invoke the testing tool with:
#
#   python3 testing_tool.py -f 1.in java MyClass
#
# The tool is provided as-is, and you should feel free to make
# whatever alterations or augmentations you like to it.
#
# The tool attempts to detect and report common errors, but it is not an
# exhaustive test. It is not guaranteed that a program that passes this testing
# tool will be accepted.
#

import argparse
import subprocess
import traceback

import sys


def write(p, line):
    assert p.poll() is None, 'Program terminated early'
    print('Write: {}'.format(line), flush=True)
    p.stdin.write('{}\n'.format(line))
    p.stdin.flush()


def read(p):
    assert p.poll() is None, 'Program terminated early'
    line = p.stdout.readline().strip()
    assert line != '', 'Read empty line or closed output pipe'
    print('Read: {}'.format(line), flush=True)
    return line


parser = argparse.ArgumentParser(description='Testing tool for problem Ducky Debugging.')
parser.add_argument('-f', dest='inputfile', metavar='inputfile', default=None, type=argparse.FileType('r'),
                    required=True, help='The input file to use.')
parser.add_argument('program', nargs='+', help='Invocation of your solution')

args = parser.parse_args()

with args.inputfile as f:
    lines = []
    for line in f.readlines():
        if line.endswith('\n'): line = line[:-1]
        assert len(line) > 0, 'Line in input file must not be empty!'
        assert line[-1] in '.?!', 'Line in input file must end with period, question mark, or exclamation mark.'
        lines.append(line)
    assert len(lines) > 0, 'Input file is empty'
    assert lines[-1] == 'I quacked the code!', 'Input file does not end with "I quacked the code!".'
    for line in lines[:-1]:
        assert line[-1] in '.?', 'Only the last line may end in an exclamation mark.'

with subprocess.Popen(" ".join(args.program), shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE,
                      universal_newlines=True) as p:
    try:
        for line in lines:
            write(p, line)
            if line[-1] != '!':
                assert read(p) in ['*Nod*', 'Quack!']

        assert p.stdout.readline() == '', 'Your submission printed extra data after finding solution'
        assert p.wait() == 0, 'Your submission did not exit cleanly after finishing'

        sys.stdout.write('\nSuccess.\n'.format(p.wait()))
    except:
        print()
        traceback.print_exc()
        print()
        try:
            p.wait(timeout=2)
        except subprocess.TimeoutExpired:
            print('Killing your submission after 2 second timeout.')
            p.kill()
    finally:
        sys.stdout.flush()
        sys.stderr.flush()
        sys.stdout.write('Exit code: {}\n'.format(p.wait()))
        sys.stdout.flush()
