/* ************************************************************************** */
/*                                                                            */
/*                                                        :::      ::::::::   */
/*   ft_hashmap.c                                       :+:      :+:    :+:   */
/*                                                    +:+ +:+         +:+     */
/*   By: thrieg <thrieg@student.42mulhouse.fr>      +#+  +:+       +#+        */
/*                                                +#+#+#+#+#+   +#+           */
/*   Created: 2024/11/07 12:14:50 by thrieg            #+#    #+#             */
/*   Updated: 2025/02/16 19:08:34 by thrieg           ###   ########.fr       */
/*                                                                            */
/* ************************************************************************** */

#include "ft_hashmap_private.h"

//allocates an array of initial_size, and sets nb_elems/hashmap_size to 0
//cmp is a function that returns 0 if both keys are the same
//returns NULL if any allocation fails
t_hashmap	*ft_create_hashmap(
size_t initial_size,
void (*free_key)(void *),
void (*free_value)(void *),
int (*cmp)(void *key, void *key_to_compare))
{
	t_hashmap	*map;

	if (initial_size == 0)
		return (NULL);
	map = malloc(sizeof(t_hashmap));
	if (!map)
		return (NULL);
	map->nb_elems = 0;
	map->hashmap_size = initial_size;
	map->cmp = cmp;
	map->free_key = free_key;
	map->free_value = free_value;
	map->table = ft_calloc((initial_size), sizeof(t_list *));
	if (!(map->table))
		return (free(map), NULL);
	return (map);
}

// Inserts a key-value pair into the hashmap, 
//returns 0 for an error, 1 for remplaced value for a key and 2 for new key
int	ft_hashmap_insert_element(
t_hashmap *map,
void *key,
void *value,
size_t hash)
{
	size_t			index;
	t_list			*current;
	t_hash_element	*element;

	if (!map || !key)
		return (0);
	if ((map->nb_elems * 100) > map->hashmap_size * HASHMAP_FILL_PERCENT_MAX)
		ft_resize_hashmap(map);
	index = hash % map->hashmap_size;
	current = map->table[index];
	while (current)
	{
		if (map->cmp(((t_hash_element *)(current->content))->key, key) == 0)
		{
			map->free_value(((t_hash_element *)(current->content))->value);
			return (((t_hash_element *)(current->content))->value = value, 1);
		}
		current = current->next;
	}
	element = ft_create_hash_element(key, value, hash);
	if (!element)
		return (0);
	if (!ft_add_hashmap_element(index, map, element))
		return (ft_free_hash_element(element, map), 0);
	return (2);
}

//returns 1 if the element has been deleted
//returns 0 if the element has not been found
int	ft_delete_hashmap_element(t_hashmap *map, void *key, size_t hash)
{
	size_t			index;
	t_list			*curr;
	t_list			*prev;
	t_hash_element	*elem;

	if (!map || !key || map->hashmap_size == 0)
		return (0);
	if (((map->nb_elems * 100) / map->hashmap_size)
		<= HASHMAP_FILL_PERCENT_BEFORE_SHRINK)
		ft_shrink_hashmap(map);
	index = hash % map->hashmap_size;
	curr = map->table[index];
	prev = NULL;
	while (curr)
	{
		elem = (t_hash_element *)curr->content;
		if (map->cmp(elem->key, key) == 0)
		{
			return (ft_delete_hashmap_list_node(curr, prev, index, map), 1);
		}
		prev = curr;
		curr = curr->next;
	}
	return (0);
}

//returns a pointer to the value of key, or a NULL pointer if not found
void	*ft_lookup_hashmap_element(t_hashmap *map, void *key, size_t hash)
{
	size_t			index;
	t_list			*curr;
	t_hash_element	*elem;

	if (!map || !key || map->hashmap_size == 0)
		return (NULL);
	index = hash % map->hashmap_size;
	curr = map->table[index];
	while (curr)
	{
		elem = (t_hash_element *)curr->content;
		if (map->cmp(elem->key, key) == 0)
			return (elem->value);
		curr = curr->next;
	}
	return (NULL);
}

//frees the entire hashmap and then returns NULL once done
void	*ft_free_hashmap(t_hashmap *map)
{
	size_t			index;
	t_list			*curr;
	t_list			*next;
	t_hash_element	*elem;

	if (!map)
		return (NULL);
	if (!map->table)
		return (free(map), NULL);
	index = 0;
	while (index < map->hashmap_size)
	{
		curr = map->table[index];
		while (curr)
		{
			next = curr->next;
			elem = (t_hash_element *)curr->content;
			ft_free_hash_element(elem, map);
			free(curr);
			curr = next;
		}
		index++;
	}
	free(map->table);
	return (free(map), NULL);
}
