#!/usr/bin/env python3
#
# Testing tool for the Lock In problem
#
# Usage:
#
#   python3 testing_tool.py -f inputfile <program invocation>
#
#
# Use the -f parameter to specify the input file, e.g. 1.in.
# The input file should contain 100_000 lines, each with a number i (1<=i<=100).
# Each chunk of 10 lines will be interpreted as a game, so these should be pairwise distinct.
# Note that no input files are included along with this testing tool.

# You can compile and run your solution as follows:

# C++:
#   g++ solution.cpp
#   python3 testing_tool.py -f 1.in ./a.out

# Python:
#   python3 testing_tool.py -f 1.in python3 ./solution.py

# Java:
#   javac solution.java
#   python3 testing_tool.py -f 1.in java solution

# Kotlin:
#   kotlinc solution.kt
#   python3 testing_tool.py -f 1.in kotlin solutionKt


# 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

parser = argparse.ArgumentParser(
    description="Testing tool for problem Lock In. Read the comments in this file for instructions."
)
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,
    subprocess.Popen(
        " ".join(args.program),
        shell=True,
        stdout=subprocess.PIPE,
        stdin=subprocess.PIPE,
        universal_newlines=True,
    ) as p,
):
    assert p.stdin is not None and p.stdout is not None
    p_in = p.stdin
    p_out = p.stdout

    def write(line: str):
        assert p.poll() is None, "Program terminated early"
        print(f"Write: {line}", flush=True)
        p_in.write(f"{line}\n")
        p_in.flush()

    def read() -> str:
        line = p_out.readline().strip()
        if line == "":
            assert p.poll() is None, "Program terminated early"
        assert line != "", "Read empty line or closed output pipe"
        print(f"Read: {line}", flush=True)
        return line

    nums = f.readlines()

    # Convert the list of lines to a list of numbers
    assert len(nums) % 10 == 0, "The number of lines should be a multiple of 10."
    for i, num in enumerate(nums):
        try:
            nums[i] = int(num)
            assert 1 <= nums[i] <= 100, "One of the numbers in the file is not between 1 and 100."
        except ValueError:
            print("One of the lines in the input file is not a number.")

    chunks = [nums[i : i + 10] for i in range(0, len(nums), 10)]
    wins = 0

    assert len(chunks) == 10_000, f"The number of games is {len(chunks)}, not 10000."

    for chunk in chunks:
        assert len(set(chunk)) == 10, "Some game contained a duplicate value"

    # Simulate interaction
    try:
        for chunk in chunks:
            game = [-1] * 10
            for num in chunk:
                write(num)
                i = int(read())

                assert game[i - 1] == -1, "Your program tried to write to an already written index."

                game[i - 1] = num

            if sorted(game) == game:
                wins += 1
                write("win")
            else:
                write("loss")

        print()
        print(f"Won {wins} game{'s' if wins != 1 else ''} (required: 100 out of 10000)")
        extra = p_out.readline()
        assert extra == "", (
            f"Your submission printed extra data after finding a solution: '{extra[:100].strip()}{'...' if len(extra) > 100 else ''}'"
        )
        print(f"Exit code: {p.wait()}", flush=True)
        assert p.wait() == 0, "Your submission did not exit cleanly after finishing"

    except AssertionError as e:
        print()
        print(f"Error: {e}")
        print()
        print("Killing your submission.", flush=True)
        p.kill()
        exit(1)

    except ValueError as e:
        print()
        print(f"Error: {e}")
        print("Your program prints a value that could not be converted to an index.")
        print()
        print("Killing your submission", flush=True)
        p.kill()
        exit()

    except Exception:
        print()
        print("Unexpected error:")
        traceback.print_exc()
        print()
        print("Killing your submission.", flush=True)
        p.kill()
        exit(1)
