"""
Jason Yau 
09/03/2025

Use to quickly run one student source code from Webcourses on Eustis and output results using stdout.

====================================

Usage:

Go to assignments, P0, Download a .c source code submission.

Copy wordlecounting.c file to temp Eustis folder like so (This assumes ~/temp/ exists already. If not, you will need to create the directory.): 
scp wordlecounting.c <YOUR_NID>@eustis.eecs.ucf.edu:~/temp/

Copy this python script (p0.py) to temp Eustis folder like so:
scp -r <path of p0.py> <YOUR_NID>@eustis.eecs.ucf.edu:~/temp/

Copy both test cases zip file to temp Eustis folder like so:
scp -r <path of p0-sample.zip> <YOUR_NID>@eustis.eecs.ucf.edu:~/temp/
scp -r <path of p0-secret.zip> <YOUR_NID>@eustis.eecs.ucf.edu:~/temp/

ssh into eustis
ssh <YOUR_NID>@eustis.eecs.ucf.edu

Run the python script.
python3 ~/temp/p0_ind.py 

====================================

Make sure to delete all created files afterwards to be safe.
"""


import os
import glob
from zipfile import ZipFile
import shutil
import subprocess
import re

def normalize(s: str) -> str:
    # Normalize whitespace: strip leading/trailing, collapse multiple spaces/newlines
    return "\n".join(line.strip() for line in s.strip().splitlines())

script_path = os.path.abspath(__file__)
script_directory = os.path.dirname(script_path)

extracted_path = f"{script_directory}/extracted"
if (len(glob.glob(extracted_path)) != 0):
    shutil.rmtree(extracted_path)

sample_cases_zip = glob.glob(f"{script_directory}/p0-sample.zip")
if len(sample_cases_zip) != 1:
    print(f"p0-sample.zip not found. Exiting.")
    exit(1)
with ZipFile(sample_cases_zip[0], 'r') as zip_object:
    zip_object.extractall(extracted_path)

secret_cases_zip = glob.glob(f"{script_directory}/p0-secret.zip")
if len(secret_cases_zip) != 1:
    print(f"p0-secret.zip not found. Exiting.")
    exit(1)
with ZipFile(secret_cases_zip[0], 'r') as zip_object:
    zip_object.extractall(extracted_path)

input_files = sorted(glob.glob(f"{extracted_path}/p0-*/wordlescoring*.in"))
output_files = sorted(glob.glob(f"{extracted_path}/p0-*/wordlescoring*.out"))
# Make sure we don't screw this part up.
assert len(input_files) == len(output_files)
for i in range(len(input_files)):
    assert input_files[i].split(".in")[0] in output_files[i].split(".out")[0]


source_code_files = glob.glob(f"{script_directory}/*.c")+glob.glob(f"{script_directory}/*.C")
if len(source_code_files) != 1:
    print(f"The number of .c source code files is not 1. Exiting...")
    exit(1)
source_code_file = source_code_files[0]


executable = f"{source_code_file.split(".c")[0]}.exe"
gcc_result = subprocess.run(["gcc", source_code_file, "-o", executable], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
compiled = gcc_result.returncode == 0
comment = ""
passed_cases = 0

if compiled:
    for i in range(len(input_files)):
        with open(input_files[i], "r") as fin:
            try:
                execute_result = subprocess.run([executable], stdin=fin, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=10, text=True, universal_newlines=True, encoding='mac_roman')
                if i == 0:
                    print(execute_result.stdout)
                if normalize(execute_result.stdout) == normalize(open(output_files[i], "r").read()):
                    comment += f"Test case #{i+1}: PASSED\n"
                    passed_cases += 1
                else:
                    comment += f"Test case #{i+1}: FAILED (INCORRECT OUTPUT)\n"
            except subprocess.TimeoutExpired:
                comment += f"Test case #{i+1}: FAILED (TIME LIMIT EXCEEDED)\n"
    os.remove(executable)
else:
    comment += "Did not compile.\n"
comment += f"Test cases overall: {passed_cases}/{len(input_files)}"

print(comment)

shutil.rmtree(extracted_path)