#!/usr/bin/env python

import argparse
import os
import re


def main():
    p = argparse.ArgumentParser()
    p.add_argument("name", help="The name of the new benchmark to be created")
    args = p.parse_args()

    # adds benchmark to `CMakeLists.txt`
    update_cmakelists(args.name)
    # create benchmark Swift file
    create_benchmark_file(args.name)
    # imports the benchmark module in `main.swift`
    add_import_benchmark(args.name)
    # registers the benchmark with the driver in `main.swift`
    add_register_benchmark(args.name)


def update_cmakelists(name):
    """Adds a new entry to the `CMakeLists.txt` file with the given
    benchmark name.
    """
    relative_path = create_relative_path("../CMakeLists.txt")

    file_contents = []
    with open(relative_path, "r") as f:
        file_contents = f.readlines()

    file_new_contents = insert_line_alphabetically(
        name,
        "    single-source/" + name + "\n",
        file_contents,
        r"    single-source\/([a-zA-Z]+)",
    )
    with open(relative_path, "w") as f:
        for line in file_new_contents:
            f.write(line)


def create_benchmark_file(name):
    """Creates a new Swift file with the given name based on the template
    and places it in the `single-source` directory.
    """

    template_path = create_relative_path("Template.swift")
    benchmark_template = ""
    with open(template_path, "r") as f:
        benchmark_template = "".join(f.readlines())

    # fill in template with benchmark name.
    formatted_template = benchmark_template.format(name=name)

    relative_path = create_relative_path("../single-source/")
    source_file_path = os.path.join(relative_path, name + ".swift")
    with open(source_file_path, "w") as f:
        f.write(formatted_template)


def add_import_benchmark(name):
    """Adds an `import` statement to the `main.swift` file for the new 
    benchmark.
    """
    relative_path = create_relative_path("../utils/main.swift")

    # read current contents into an array
    file_contents = []
    with open(relative_path, "r") as f:
        file_contents = f.readlines()

    # the test dependencies are placed before all benchmarks, so we have to
    # insert the benchmark in the right alphabetical order after we have seen
    # all test dependencies.
    read_test_dependencies = False
    previous_benchmark_name = None
    file_new_contents = []
    for line in file_contents:
        # check if this line is a definition of a benchmark and get its name
        match = re.search(r"import ([a-zA-Z]+)", line)
        if match and match.group(1):
            benchmark_name = match.group(1)
            # find where to insert the new benchmark in the right alphabetical
            # order.
            if (
                name < benchmark_name
                and previous_benchmark_name is None
                or name < benchmark_name
                and name > previous_benchmark_name
            ):
                if read_test_dependencies:
                    file_new_contents.append("import " + name + "\n" + line)
                else:
                    # all test dependencies are first specified, so from now
                    # on we can look where to insert the new benchmark.
                    read_test_dependencies = True
                    file_new_contents.append(line)
            else:
                file_new_contents.append(line)
            previous_benchmark_name = benchmark_name
        else:
            file_new_contents.append(line)
    with open(relative_path, "w") as f:
        for line in file_new_contents:
            f.write(line)


def add_register_benchmark(name):
    """Adds an `import` statement to the `main.swift` file for the new
    benchmark.
    """
    relative_path = create_relative_path("../utils/main.swift")

    file_contents = []
    with open(relative_path, "r") as f:
        file_contents = f.readlines()

    file_new_contents = insert_line_alphabetically(
        name,
        "registerBenchmark(" + name + ")\n",
        file_contents,
        r"registerBenchmark\(([a-zA-Z]+)\)",
    )
    with open(relative_path, "w") as f:
        for line in file_new_contents:
            f.write(line)


def insert_line_alphabetically(name, new_line, lines, regex):
    """Iterates through the given lines and executes the regex on each line to
    find where the new benchmark should be inserted with the given `new_line`.
    """
    # the name of the previous seen benchmark in order to insert the new
    # one at the correct position
    previous_benchmark_name = None
    # the new contents of the file
    updated_lines = []
    for line in lines:
        # apply regex and get name of benchmark on this line
        match = re.search(regex, line)
        if match and match.group(1):
            benchmark_name = match.group(1)
            # check if we're at the line where we have to insert the new
            # benchmark in the correct alphabetical order
            if (
                name < benchmark_name
                and previous_benchmark_name is None
                or name < benchmark_name
                and name > previous_benchmark_name
            ):
                updated_lines.append(new_line + line)
            else:
                updated_lines.append(line)
            previous_benchmark_name = benchmark_name
        else:
            updated_lines.append(line)
    return updated_lines


def create_relative_path(file_path):
    return os.path.join(os.path.dirname(__file__), file_path)


if __name__ == "__main__":
    main()
