#include <stdlib.h>
#include <string.h>
#include "hashtable.h"

// Create a hash table with the given number of buckets.
// Each bucket starts as an empty linked list (NULL).
hashtable *ht_create(int table_size)
{
    hashtable *ht = (hashtable *)malloc(sizeof(hashtable));
    ht->table_size = table_size;
    // calloc initializes all pointers to NULL
    ht->table = (node **)calloc(table_size, sizeof(node *));
    return ht;
}

// Hash function for strings.
// Same idea as str_to_int from the Python lecture:
// treat the string as a number in base 37, then mod by table_size.
int ht_hash(const char *key, int table_size)
{
    unsigned long hash = 0;
    int p = 37;
    unsigned long p_pow = 1;  // p^i
    for (int i = 0; key[i] != '\0'; i++) {
        hash += key[i] * p_pow;
        p_pow *= p;
    }
    return hash % table_size;
}

// Insert a (key, value) pair.
// Compute the bucket index, then insert into that bucket's linked list.
void ht_put(hashtable *ht, const char *key, const char *value)
{
    int idx = ht_hash(key, ht->table_size);
    // list_insert handles both "key already exists" (update)
    // and "key not found" (prepend) cases
    ht->table[idx] = list_insert(ht->table[idx], key, value);
}

// Look up a key.
// Compute the bucket index, then search that bucket's linked list.
char *ht_get(hashtable *ht, const char *key)
{
    int idx = ht_hash(key, ht->table_size);
    return list_search(ht->table[idx], key);
}

// Free the hash table and all its chains
void ht_destroy(hashtable *ht)
{
    for (int i = 0; i < ht->table_size; i++) {
        list_destroy(ht->table[i]);
    }
    free(ht->table);
    free(ht);
}
