#pragma once

#include <machinarium/mutex.h>

#include <types.h>
#include <list.h>
#include <murmurhash.h>

typedef struct od_hashmap_list_item od_hashmap_list_item_t;

/*
 * void * data should have following fmt:
 key
 value
 */

/*
 * TODO: make hashmap more simple in impl and usage
 * for ex.: remove double pointer to buckets
 * and do not require hashing at user-side
 */

/* header, first keylen bytes is key, other is value */
typedef struct {
	void *data;
	size_t len;
} od_hashmap_elt_t;

struct od_hashmap_list_item {
	od_hashmap_elt_t key;
	od_hashmap_elt_t value;
	od_list_t link;
};

extern od_hashmap_list_item_t *od_hashmap_list_item_create(void);

extern od_retcode_t od_hashmap_list_item_free(od_hashmap_list_item_t *l);

typedef struct od_hashmap_bucket {
	od_list_t items;
	mm_mutex_t mu;
} od_hashmap_bucket_t;

typedef struct od_hashmap od_hashmap_t;

typedef void (*od_hashmap_item_cb_t)(od_hashmap_list_item_t *item);

struct od_hashmap {
	size_t size;
	od_hashmap_item_cb_t dtor;
	od_hashmap_bucket_t *buckets;
};

extern od_hashmap_t *od_hashmap_create(size_t sz);
extern od_hashmap_t *od_hashmap_create_with_dtor(size_t sz,
						 od_hashmap_item_cb_t dtor);
extern od_retcode_t od_hashmap_free(od_hashmap_t *hm);
od_hashmap_elt_t *od_hashmap_find(od_hashmap_t *hm, od_hash_t keyhash,
				  od_hashmap_elt_t *key);

/* This function insert new key into hashmap
* If hashmap already contains value associated with key, 
* it will be rewritten.
*/
int od_hashmap_insert(od_hashmap_t *hm, od_hash_t keyhash,
		      od_hashmap_elt_t *key, od_hashmap_elt_t **value);

/* LOCK-UNLOCK API */
/* given key and its 
* keyhash (murmurhash etc) return pointer 
* to hashmap mutex-locked value pointer  */
od_hashmap_elt_t *od_hashmap_lock_key(od_hashmap_t *hm, od_hash_t keyhash,
				      od_hashmap_elt_t *key);

int od_hashmap_unlock_key(od_hashmap_t *hm, od_hash_t keyhash,
			  od_hashmap_elt_t *key);

/* clear hashmap */
od_retcode_t od_hashmap_empty(od_hashmap_t *hm);
