#ifndef ARC_STD_HASHTABLE_H_
#define ARC_STD_HASHTABLE_H_

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>
#include <stddef.h>

/**
 * @brief the arc hashtable data type
*/
typedef struct ARC_Hashtable ARC_Hashtable;

/**
 * @brief a node that contains a key-value reference along with a linked list like node
*/
typedef struct ARC_HashtableNode ARC_HashtableNode;
struct ARC_HashtableNode {
    void *key;
    size_t keysize;

    void *data;
    ARC_HashtableNode *node;
};

/**
 * @brief a hashing function ptr
 *
 * @param key     value to hash
 * @param keysize should be sizeof(key) before key is a void ptr
 * @param hashval value of hash, does not need to be within range of buckets
*/
typedef void (* ARC_Hashtable_Hash)(void *key, size_t *keysize, uint32_t *hashval);

/**
 * @brief key comparison function ptr
 *
 * @param key1 first key
 * @param key2 second key
 *
 * @return 0 when keys match
*/
typedef int8_t (* ARC_Hashtable_KeyCompare)(void *key1, size_t *key1size, void *key2, size_t *key2size);

/**
 * @brief callback to allow memory freeing of nodes
 *
 * @param node     node to be destroyed
 * @param userdata any data the user wants to access in the callback
*/
typedef void (* ARC_HashtableNode_DestroyExternal)(ARC_HashtableNode *node, void *userdata);

/**
 * @brief cteates ARC_Hashtable type
 *
 * @param htable     where to store data
 * @param bucketsize num of nodes to create in inital table
 * @param hash       hashing function to be used, if set to NULL, CRC32 will be used
 * @param compare    comparison functon for checking keys, if set to NULL, addresses will be compared
*/
void ARC_Hashtable_Create(ARC_Hashtable **htable, uint32_t bucketsize, ARC_Hashtable_Hash hash, ARC_Hashtable_KeyCompare compare);

/**
 * @brief destroys ARC_Hashtable type
 *
 * @param htable   htable that will be destroyed
 * @param external function to allow external freeing of nodes, can be NULL
 * @param userdata any data the user wants access to in the callback
*/
void ARC_Hashtable_Destroy(ARC_Hashtable *htable, ARC_HashtableNode_DestroyExternal external, void *userdata);

/**
 * @brief adds value to hastable
 *
 * @param htable  ARC_Hashtable to add to
 * @param key     key for node that is being added
 * @param keysize sizeof key before it is passed into a void *
 * @param data    data for node that is being added
*/
void ARC_Hashtable_Add(ARC_Hashtable *htable, void *key, size_t keysize, void *data);

/**
 * @brief gets value from hashtable by key
 *
 * @param htable  table to get value from
 * @param key     key to get value from table 
 * @param keysize sizeof key before it is passed into a void *
 * @param data    data retrieved from table
*/
void ARC_Hashtable_Get(ARC_Hashtable *htable, void *key, size_t keysize, void **data);

/**
 * @brief removes value from hashtable
 *
 * @param htable  ARC_Hashtable to remove from
 * @param key     key of data to remove from hash table
 * @param keysize sizeof key before it is passed into a void *
 * @param external function to allow external freeing of data, can be NULL
 * @param userdata any data the user wants access to in the callback
*/
void ARC_Hashtable_Remove(ARC_Hashtable *htable, void *key, size_t keysize, ARC_HashtableNode_DestroyExternal external, void *userdata);

#ifdef __cplusplus
}
#endif

#endif //ARC_STD_HASHTABLE_H_