import time
import csv
import random

from generator.randomnames import generate_dataset, name_pool

from structures.LinkedList import ll_insert, ll_find, ll_delete
from structures.HashTable import ht_insert, ht_find, ht_delete
from structures.BinaryTree import bst_insert, bst_find, bst_delete


def build_search_set():
    return random.sample(name_pool, min(100, len(name_pool))) + [
        f"Fake_{i}" for i in range(10)
    ]


def run_once(insert_f, find_f, delete_f, records, search_set, delete_set):
    data = None

    # INSERT
    start = time.perf_counter()
    for name, phone in records:
        data = insert_f(data, name, phone)
    insert_time = time.perf_counter() - start

    # FIND
    start = time.perf_counter()
    for name in search_set:
        find_f(data, name)
    find_time = time.perf_counter() - start

    # DELETE
    start = time.perf_counter()
    for name in delete_set:
        data = delete_f(data, name)
    delete_time = time.perf_counter() - start

    return insert_time, find_time, delete_time


def run_all():
    results = []

    structures = [
        ("LinkedList", ll_insert, ll_find, ll_delete),
        ("HashTable", ht_insert, ht_find, ht_delete),
        ("BST", bst_insert, bst_find, bst_delete)
    ]

    for order_name, sorted_flag in [("shuffled", False), ("sorted", True)]:

        for struct_name, ins, fnd, dlt in structures:

            for run_id in range(5):

                print(f"{struct_name} | {order_name} | run {run_id + 1}")

                records = generate_dataset(10000, sorted_flag=sorted_flag)

                search_set = build_search_set()
                delete_set = random.sample(name_pool, 50)

                insert_t, find_t, delete_t = run_once(
                    ins, fnd, dlt,
                    records,
                    search_set,
                    delete_set
                )

                results.append([
                    struct_name,
                    order_name,
                    "insert",
                    run_id + 1,
                    insert_t
                ])

                results.append([
                    struct_name,
                    order_name,
                    "find",
                    run_id + 1,
                    find_t
                ])

                results.append([
                    struct_name,
                    order_name,
                    "delete",
                    run_id + 1,
                    delete_t
                ])

    return results


def save_csv(results):
    with open("results.csv", "w", newline="") as f:
        writer = csv.writer(f)

        writer.writerow([
            "Structure",
            "Order",
            "Operation",
            "Run",
            "Time"
        ])

        writer.writerows(results)


if __name__ == "__main__":
    results = run_all()
    save_csv(results)

    print("\nDONE -> results.csv created")