#include "hm.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

static int hash(char *key, int capacity) {
    int h = 0;
    for (int i = 0; i < (int)strlen(key); ++i) {
        h = (h + key[i] * 43) % capacity;
    }
    return h;
}

// allocate memory of the table to capacity
void hm_init(struct hm_t *hm, int capacity) {
    hm->table = malloc(sizeof(*(hm->table)) * capacity);
    hm->capacity = capacity;
    hm->size = 0;
    for (int i = 0; i < hm->capacity; ++i) {
        hm->table[i].state = empty;
    }
}

// insert the key - value pair: if key is already present
// we erase the old value. if the table is full we return false
bool hm_insert(struct hm_t *hm, char *key, char *value) {
    int index = hash(key, hm->capacity);
    int initial_index = index;
    while (hm->table[index].state == occupied && strncmp(hm->table[index].key, key, MAX_LEN) != 0) {
        index = (index + 1) % hm->capacity;
        if (index == initial_index) {
            return false;
        }
    }
    hm->table[index].state = occupied;
    strncpy(hm->table[index].key, key, MAX_LEN);
    strncpy(hm->table[index].value, value, MAX_LEN);
    hm->size += 1;

    return true;
}

// changes the state of the table at the "key" position to deleted
void hm_delete(struct hm_t *hm, char *key) {
    int index = hash(key, hm->capacity);
    int initial_index = index;
    while (hm->table[index].state != empty) {
        if (hm->table[index].state == occupied && strncmp(hm->table[index].key, key, MAX_LEN) == 0)
        {
            hm->table[index].state = deleted;
            hm->size -= 1;
        }
        index = (index + 1) % hm->capacity;
        if (index == initial_index) {
            return;
        }
    }
}

// returns the value linked to the key if present
// return NULL otherwise
char *hm_get(struct hm_t hm, char *key) {
    int index = hash(key, hm.capacity);
    int initial_index = index;
    while (hm.table[index].state != empty) {
        if (hm.table[index].state == occupied && strncmp(hm.table[index].key, key, MAX_LEN) == 0) {
            return hm.table[index].value;
        }
        index = (index + 1) % hm.capacity;
        if (index == initial_index) {
            return NULL;
        }
    }

    return NULL;
}

// prints the state of the table
void hm_print(struct hm_t hm) {
    for (int i = 0; i < hm.capacity; ++i) {
        if (hm.table[i].state == empty) {
            printf("[%d]: empty\n", i);
        } else if (hm.table[i].state == occupied) {
            printf("[%d]: occupied, %s, %s\n", i, hm.table[i].key, hm.table[i].value);
        } else {
            printf("[%d]: deleted, %s, %s\n", i, hm.table[i].key, hm.table[i].value);
        }
    }
}

// frees ressources
void hm_destroy(struct hm_t *hm) {
    free(hm->table);
    hm->table = NULL;
    hm->size = -1;
    hm->capacity = -1;
}
