#!/usr/bin/env python3

from __future__ import print_function
import sys
import re
import argparse
from collections import defaultdict


def color(text, c):
    """
    Add color on the keyword that identifies the state of the test
    """
    if sys.stdout.isatty():
        clear = "\033[0m"

        colors = {
            "red": "\033[1m\033[91m",
            "yellow": "\033[1m\033[93m",
            "green": "\033[1m\033[92m",
        }
        return colors[c] + text + clear
    else:
        return text


def parse_args():
    parser = argparse.ArgumentParser(description="Report on test results")
    parser.add_argument('--summary', action="store_true",
                        help="Display only the totals in each category")
    parser.add_argument('tapfile', default="all.log", nargs="?",
                        help="File containing TAP output")
    return parser.parse_args()


def print_category(tests):
    if not cmd_args.summary:
        for key in sorted(tests):
            print("%-32s %4d" % (key, tests[key]))


def pad(i):
    return " " * i


if __name__ == "__main__":
    cmd_args = parse_args()

    errors = defaultdict(int)
    skipped = defaultdict(int)
    expected = defaultdict(int)
    unexpected = defaultdict(int)
    passed = defaultdict(int)

    file = re.compile("^# (?:./)?(\S+\.t)(?:\.exe)?$")
    timestamp = re.compile("^# (\d+(?:\.\d+)?) ==>.*$")

    expected_fail = re.compile(r"^not ok.*?#\s*TODO", re.I)
    unexpected_pass = re.compile(r"^not ok .*?#\s*FIXED", re.I)
    skip = re.compile(r"^ok .*?#\s*skip", re.I)
    ok = re.compile(r"^ok ", re.I)
    not_ok = re.compile(r"^not ok", re.I)
    comment = re.compile(r"^#")
    plan = re.compile(r"^1..\d+\s*(?:#.*)?$")

    start = None
    stop = None

    with open(cmd_args.tapfile) as fh:
        for line in fh:
            if start is None:
                # First line contains the starting timestamp
                start = float(timestamp.match(line).group(1))
                continue

            match = file.match(line)
            if match:
                filename = match.group(1)

            elif expected_fail.match(line):
                expected[filename] += 1

            elif unexpected_pass.match(line):
                unexpected[filename] += 1

            elif skip.match(line):
                skipped[filename] += 1

            # It's important these come last, since they're subpatterns of the above

            elif ok.match(line):
                passed[filename] += 1

            elif not_ok.match(line):
                errors[filename] += 1

            elif comment.match(line):
                pass

            elif plan.match(line):
                pass

            else:
                # Uncomment if you want to see malformed things we caught as well...
                # print(color("Malformed TAP (" + filename + "): " + line, "red"))
                pass

        # Last line contains the ending timestamp
        stop = float(timestamp.match(line).group(1))

    v = "{0:>5d}"
    passed_str = "Passed:" + pad(24)
    passed_int = v.format(sum(passed.values()))
    error_str = "Failed:" + pad(24)
    error_int = v.format(sum(errors.values()))
    unexpected_str = "Unexpected successes:" + pad(10)
    unexpected_int = v.format(sum(unexpected.values()))
    skipped_str = "Skipped:" + pad(23)
    skipped_int = v.format(sum(skipped.values()))
    expected_str = "Expected failures:" + pad(13)
    expected_int = v.format(sum(expected.values()))
    runtime_str = "Runtime:" + pad(20)
    runtime_int = "{0:>8.2f} seconds".format(stop - start)

    if cmd_args.summary:
        print(color(passed_str, "green"), passed_int)
        print(color(error_str, "red"), error_int)
        print(color(unexpected_str, "red"), unexpected_int)
        print(color(skipped_str, "yellow"), skipped_int)
        print(color(expected_str, "yellow"), expected_int)
        print(runtime_str, runtime_int)

    else:
        print(color(error_str, "red"))
        print_category(errors)
        print()
        print(color(unexpected_str, "red"))
        print_category(unexpected)
        print()
        print(color(skipped_str, "yellow"))
        print_category(skipped)
        print()
        print(color(expected_str, "yellow"))
        print_category(expected)

    # If we encoutered any failures, return non-zero code
    sys.exit(1 if int(error_int) or int(unexpected_int) else 0)
