#!/usr/bin/env python3

import os
import sys
from dist import compare_srcfile

##### LOCAL VARIABLES AND CONSTANTS #####
show_info = False
show_missing_files = False

nb_ignored_files = 0
nb_missing_files = 0
nb_valid_files = 0

sorted_tags = []
test_files = []

tagged_files = {}
valid_tags = []
test_type_tags = ['data_correctness', 'functional_correctness']

C_FILE_TYPE = 0
PY_FILE_TYPE = 1
END_TAG = "[END_TAGS]"
IGNORE_FILE = "ignored_file"
START_TAG = "[TEST_TAGS]"
#####

##### FUNCTIONS #####
def validate_tag(tag, filename):
    split_tag = tag.split(":")
    # Ensure the array isn't too long.
    if (len(split_tag) > 3):
        print("Tag contains too many sub tags: " + tag + " filename: " + filename);
        exit(1)

    # Walk the pieces of the tag and ensure they exist in test_tags.ok.
    for sub_tag in split_tag:
        if not sub_tag in valid_tags:
            print(
                "Invalid sub tag found: " + sub_tag + " in tag: " + tag + " filename: " + filename)
            exit(1)

def format_tag(tag):
    return tag.replace("_", " ").title()
#####

##### PROCESS ARGS #####
for arg in sys.argv:
    if arg == "-h":
        print("Usage: python3 test_tag.py [options]")
        print("Options:")
        print("\t-i\tShow info")
        print("\t-p\tShow files with no test tags")
        exit(1)
    elif arg == "-i":
        show_info = True
    elif arg == "-p":
        show_missing_files = True
#####

##### GET ALL TEST FILES #####
for root, dirs, files in os.walk("../test/"):
    path = root.split(os.sep)
    for file in files:
        filename = os.path.join('/'.join(path), file)
        if filename.endswith("main.c") or filename.endswith(".py"):
            test_files.append(filename)
#####

##### RETRIEVE VALID TAGS #####
validation_file = open("test_tags.ok", "r")

# The validation file contains an alphabetized list of valid tag words, with one word per line.
tags = validation_file.readlines()
tags = [tag.replace('\n', '') for tag in tags]

for tag in tags:
    valid_tags.append(tag)

# Check that the validation file is ordered.
if not all(tags[i] <= tags[i+1] for i in range(len(tags)-1)):
    print("Error: test_tags.ok is not alphabetically ordered!")
    exit(1)

validation_file.close()
#####

##### PARSE TEST FILES #####
for filename in test_files:
    input_file = open(filename, "r")
    lines = input_file.readlines()

    in_tag_block = False
    is_file_ignored = False
    is_file_tagged = False

    # Read line by line
    for line in lines:
        # Format line
        line = line.replace('\n', '').replace('\r', '') \
                   .replace(' ', '').replace('#', '') \
                   .replace('*', '')

        # Check if line is valid
        if not line:
            # Check if invalid line after START_TAG
            if in_tag_block == True:
                print("Syntax error in file: " + filename)
                exit(1)
            else:
                continue

        # Check if end of test tag
        if END_TAG in line:
            # END_TAG should not be before START_TAG
            if in_tag_block == False:
                print("Syntax error in file: " + filename + ". Unexpected tag: " + END_TAG)
                exit(1)
            # END_TAG should not be met before a test tag
            if is_file_ignored == False and is_file_tagged == False:
                print("Syntax error in file: " + filename + ". Missing test tag.")
                exit(1)
            nb_valid_files = nb_valid_files + 1
            # Go to next file
            break

        # Check if start of test tag
        if START_TAG in line:
            # Only one START_TAG is allowed
            if in_tag_block == True:
                print("Syntax error in file: " + filename + ". Unexpected tag: " + START_TAG)
                exit(1)
            in_tag_block = True
            continue

        if in_tag_block == True:
            tag = line
            # Check if file is ignored
            if is_file_ignored == True:
                print("Unexpected value in ignored file: " + filename)
                exit(1)
            if tag == IGNORE_FILE:
                nb_ignored_files = nb_ignored_files + 1
                is_file_ignored = True
                continue

            # Validate the tag's correctness.
            validate_tag(tag, filename)
            is_file_tagged = True

            skip_adding_test_type = False
            # Add the test type to the tag if it wasn't already.
            for test_type_tag in test_type_tags:
                if (tag.startswith(test_type_tag)):
                    skip_adding_test_type = True
                    break

            if not skip_adding_test_type:
                if filename.endswith(".c"):
                    tag = test_type_tags[C_FILE_TYPE] + ":" + tag
                if filename.endswith(".py"):
                    tag = test_type_tags[PY_FILE_TYPE] + ":" + tag

            # Check if current tag has already matched test files
            if tag in tagged_files:
                tagged_files[tag].append(filename)
            else:
                tagged_files[tag] = [filename]

    if is_file_ignored == False and is_file_tagged == False:
        nb_missing_files = nb_missing_files + 1
        if show_missing_files == True:
            print("Missing test tag in file: " + filename)

    input_file.close()
#####

##### GENERATE TEST COVERAGE MD #####
tmp_filename = '__tmp_test_tag' + str(os.getpid())
tfile = open(tmp_filename, 'w')

# Sort tags
sorted_tags = list(tagged_files.keys())
sorted_tags.sort()

for test_type_tag in test_type_tags:
    tfile.write("## " + format_tag(test_type_tag) + " tests:\n\n")
    tfile.write("|Component|Sub-component|Existing tests|\n")
    tfile.write("|---|---|---|\n")

    for tag in sorted_tags:
        # Split the tag.
        current_tag = tag.split(":")
        if (current_tag[0] != test_type_tag):
            continue

        # Parse and format tag.
        component = format_tag(current_tag[1])
        functionality = ""

        # The end tag is optional.
        if (len(current_tag) == 3):
            functionality = format_tag(current_tag[2])

        # Relative path to test files
        link = ""
        # Sort the filenames associated to the current tag
        tagged_files[tag].sort()
        for name in tagged_files[tag]:
            link += "[" + os.path.basename(name) + "](" + name + "), "
        # Remove the extra ", " at the end
        link = link[:-2]

        # Write to output
        tfile.write('|' + component + '|' + \
                        functionality + '|' + link + '\n')
tfile.close()
compare_srcfile(tmp_filename, "../test/test_coverage.md")
#####

##### STATS #####
if show_info == True:
    print("Tagged files:\t" + str(nb_valid_files - nb_ignored_files))
    print("Missing files:\t" + str(nb_missing_files))
    print("Ignored files:\t" + str(nb_ignored_files))
    print("Total files:\t" + str(nb_valid_files + nb_missing_files))
#####

# Enforce tagging
#if nb_missing_files > 0:
#    print("Files missing a tag: " + str(nb_missing_files))
#    if show_missing_files == False:
#        print("Call \'python3 test_tag.py -p\' to list all files with no tags")
