import random
import time
import csv
import os

# --------------------- Реализация связного списка (взята из t1_1) ---------------------
def ll_insert_begin(head, name, phone):
# Вставка узла в начало списка. Возвращает новую голову.
    new_node = {'name': name, 'phone': phone, 'next': head}
    return new_node

def ll_find(head, name):
# Поиск телефона по имени. Возвращает phone или None.
    current = head
    while current:
        if current['name'] == name:
            return current['phone']
        current = current['next']
    return None

def ll_delete(head, name):
# Удаление узла по имени. Возвращает новую голову.
    if head is None:
        return None
    if head['name'] == name:
        return head['next']
    current = head
    while current['next']:
        if current['next']['name'] == name:
            current['next'] = current['next']['next']
            return head
        current = current['next']
    return head

def ll_list_all(head):
# Собирает все записи в список и сортирует по имени.
    records = []
    current = head
    while current:
        records.append((current['name'], current['phone']))
        current = current['next']
    records.sort(key=lambda x: x[0])
    return records

# --------------------- Реализация хеш-таблицы ---------------------
class HashTable:
    def __init__(self, size=2000):
        self.size = size
        self.buckets = [None] * size  # каждый bucket — голова связного списка

    def _hash(self, name):
        # Простая хеш-функция: сумма кодов символов по модулю размера.
        return sum(ord(ch) for ch in name) % self.size

    def insert(self, name, phone):
        index = self._hash(name)
        # Вставляем в начало связного списка в данном bucket'е
        self.buckets[index] = ll_insert_begin(self.buckets[index], name, phone)

    def find(self, name):
        index = self._hash(name)
        return ll_find(self.buckets[index], name)

    def delete(self, name):
        index = self._hash(name)
        self.buckets[index] = ll_delete(self.buckets[index], name)

    def list_all(self):
        # Собирает все записи из всех bucket'ов и сортирует по имени.
        all_records = []
        for head in self.buckets:
            current = head
            while current:
                all_records.append((current['name'], current['phone']))
                current = current['next']
        all_records.sort(key=lambda x: x[0])
        return all_records

# --------------------- Функции измерений ---------------------
def generate_data(N=10000):
    records = []
    for i in range(N):
        name = f"User_{i:05d}"
        phone = f"8{random.randint(9000000000, 9999999999)}"
        records.append((name, phone))
    return records

def measure_insert(records, sort_order='random'):
    # Измеряет время вставки в хеш-таблицу.
    # sort_order: 'random' или 'sorted' — порядок передаваемых записей.
    ht = HashTable(size=2000)
    start = time.perf_counter()
    for name, phone in records:
        ht.insert(name, phone)
    end = time.perf_counter()
    return end - start

def measure_find(records, test_names):
    # Поиск 110 записей в уже заполненной хеш-таблице.
    ht = HashTable(size=2000)
    for name, phone in records:
        ht.insert(name, phone)
    start = time.perf_counter()
    for name in test_names:
        ht.find(name)
    end = time.perf_counter()
    return end - start

def measure_delete(records, delete_names):
    # Удаление 50 записей из хеш-таблицы (среднее время одного удаления).
    times = []
    for name in delete_names:
        ht = HashTable(size=2000)
        for n, p in records:
            ht.insert(n, p)
        start = time.perf_counter()
        ht.delete(name)
        end = time.perf_counter()
        times.append(end - start)
    return sum(times) / len(times)

# --------------------- Основная функция ---------------------
def main():
    N = 10000
    records = generate_data(N)

    # Перемешанные и отсортированные копии
    records_shuffled = records.copy()
    random.shuffle(records_shuffled)
    records_sorted = sorted(records, key=lambda x: x[0])

    # Имена для поиска (100 существующих + 10 несуществующих)
    existing_names = random.sample([rec[0] for rec in records], 100)
    non_existing = [f"None_{i}" for i in range(10)]
    test_names = existing_names + non_existing

    # Имена для удаления (50 случайных)
    delete_names = random.sample([rec[0] for rec in records], 50)

    # Замеры (по 5 повторений)
    insert_shuffled_avg = 0.0
    insert_sorted_avg = 0.0
    find_avg = 0.0
    delete_avg = 0.0

    repeats = 5
    for _ in range(repeats):
        insert_shuffled_avg += measure_insert(records_shuffled, 'random')
        insert_sorted_avg += measure_insert(records_sorted, 'sorted')
        find_avg += measure_find(records, test_names)
        delete_avg += measure_delete(records, delete_names)

    insert_shuffled_avg /= repeats
    insert_sorted_avg /= repeats
    find_avg /= repeats
    delete_avg /= repeats

    # Подготовка строк для CSV
    new_rows = [
        ["Hash table", "случайный", "вставка (в начало)", insert_shuffled_avg],
        ["Hash table", "отсортированный", "вставка (в начало)", insert_sorted_avg],
        ["Hash table", "любой", "поиск 110 записей", find_avg],
        ["Hash table", "любой", "удаление 50 записей (среднее)", delete_avg]
    ]

    # Определяем имя CSV-файла (там же, где и t1_1.py)
    csv_filename = "results.csv"
    file_exists = os.path.isfile(csv_filename)

    # Запись в CSV (добавление)
    with open(csv_filename, 'a', newline='', encoding='utf-8-sig') as f:
        writer = csv.writer(f, delimiter=';')
        # Если файл только что создан, сначала запишем заголовок
        if not file_exists:
            writer.writerow(["Структура", "Режим", "Операция", "Время (сек)"])
        writer.writerows(new_rows)

    print("Результаты для хеш-таблицы добавлены в", csv_filename)
    print(f"Среднее время вставки (случ. порядок): {insert_shuffled_avg:.6f} сек")
    print(f"Среднее время вставки (отсорт.):    {insert_sorted_avg:.6f} сек")
    print(f"Среднее время поиска 110 записей:    {find_avg:.6f} сек")
    print(f"Среднее время удаления 50 записей:   {delete_avg:.6f} сек")

if __name__ == "__main__":
    main()