#!/usr/bin/env python3

import argparse
import os, sys
import json
import importlib
from string import Template
import subprocess
import time

class bcolors:
    HEADER = '\033[95m'
    OKBLUE = '\033[94m'
    OKGREEN = '\033[92m'
    WARNING = '\033[93m'
    FAIL = '\033[91m'
    ENDC = '\033[0m'
    BOLD = '\033[1m'
    UNDERLINE = '\033[4m'

def clean_extension(ext):
    if ext.startswith('.'):
        ext = ext[1:]

    return ext.strip()

def clean_run_command(cmd, file_name):
    cmd = cmd.strip()
    if file_name not in cmd:
        print("Run command seems incorrect")
        sys.exit(1)

    cmd = cmd.replace(file_name, "${file_name}")
    cmd_list = cmd.split("&&")
    return cmd_list

def hr(width=80):
    for i in range(width):
        sys.stdout.write('-')
    print("")

def get_default_config_path():
    return os.getcwd() + "/config.json"

def write_config(path, config):
    with open(path, 'w') as config_file:
        json.dump(config, config_file, indent=4, sort_keys=True)

def get_config(path):
    config = None
    if not os.path.isfile(path):
        done = False
        while not done:
            print(bcolors.WARNING + "No config found;" + bcolors.ENDC +
                  " Create a new config? <Yes/No>")
            ip = sys.stdin.readline().strip()
            if ip.lower() == "no":
                sys.exit(0)
            elif ip.lower() == "yes":
                config = {}
                hr()
                print("What language are you learning, input the " +
                      bcolors.BOLD + "file extension." + bcolors.ENDC +
                      "\nExample: clj, cpp, ex.")
                config["ext"] = clean_extension(sys.stdin.readline())

                hr()
                print("How to run the file " +
                      bcolors.BOLD + "'example." + config["ext"] + "'" + bcolors.ENDC +
                      "\nExample:\n"
                      "\t python example.py\n"
                      "\t lein run example.clj\n"
                      "\t g++ example.cpp && ./a.out")
                config["run_command"] = clean_run_command(sys.stdin.readline(),
                                                          "example."+config["ext"])

                hr()
                config["z_housekeeping"] = {"tests" : {}}
                print("Config saved " + bcolors.OKGREEN + u"\u2713" + bcolors.ENDC)
                print(json.dumps(config, indent=4))
                print("If something seems incorrect, you can correct it by"
                      " editing 'config.json'")
                write_config(path, config)
                done = True
            else:
                done = False
    else:
        with open(path, 'r') as config_file:
            config = json.load(config_file)
            print("Config loaded " + bcolors.OKGREEN + u"\u2713" + bcolors.ENDC)

    return config

def check_output(file_name, config, stdin_str):
    for run_cmd in config["run_command"]:
        run_cmd = run_cmd.replace("${file_name}", file_name).split()
        proc = subprocess.Popen(run_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
        result = proc.communicate(input=stdin_str.encode("utf-8"))
        if proc.returncode != 0:
            sys.exit(result.check_returncode())

    return result[0]

def execute_tests(test_dir, config, config_path):
    sys.path.append(test_dir)

    test_files = [f for f in os.listdir(test_dir) if os.path.isfile(os.path.join(test_dir, f))]
    test_files = filter(lambda f: f != "__init__.py", test_files)
    test_files = filter(lambda f: not f.endswith(".pyc"), test_files)

    test_files = sorted(test_files, key=lambda file_name: file_name.split("-")[0])

    for test_file_name in test_files:
        if (test_file_name in config["z_housekeeping"]["tests"] and
            (config["z_housekeeping"]["tests"][test_file_name]["mtime"] >=
             os.path.getmtime(os.path.join(test_dir, test_file_name)))):
            # test passed and no change
            print(test_file_name[:-3], bcolors.OKGREEN + u"\u2713" + bcolors.ENDC)
        else:
            test = importlib.import_module("tests." + test_file_name[:-3])
            problem_statement = Template(test.get_statement())
            input_file_name = (os.getcwd() + "/" +
                               Template(test.get_input_file_name())
                               .safe_substitute(ext=config["ext"]))
            stdin_str = test.get_stdin_str()
            expected_out = test.get_expected_output()

            print(problem_statement.safe_substitute(HL=bcolors.WARNING,
                                                    ENDHL=bcolors.ENDC))
            hr()
            print("Expected Output:")
            sys.stdout.write(expected_out)
            hr()

            if not os.path.exists(input_file_name):
                open(input_file_name, 'a').close()
                print(input_file_name + " created.")
                return

            actual_output = str(check_output(input_file_name, config, stdin_str),
                                encoding="utf-8")
            if actual_output.strip() == expected_out.strip():
                print("Pass " + bcolors.OKGREEN + u"\u2713" + bcolors.ENDC)
                config["z_housekeeping"]["tests"][test_file_name] = {
                    "mtime": int(time.time())
                }
                write_config(config_path, config)
            else:
                print("act out:" + actual_output.strip())
                print("exp out:" + expected_out.strip())
                print(bcolors.FAIL + "Fail" + bcolors.ENDC)
                sys.exit(1)



if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Cute teapot tests.')
    parser.add_argument("-v", "--verbose", help="show all results", action="store_true")
    args = parser.parse_args()
    config = get_config(get_default_config_path())
    script_dir = os.path.dirname(os.path.abspath(__file__))
    test_dir = script_dir + "/tests"
    execute_tests(test_dir, config, get_default_config_path())
