/*
 * Created by jiaqiang.ye@lnxall.com
 *
 * Integer and pointer hash, with string as keys
 *
 * 2022/05/27
 */

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

#include "hash_intptr.h"

static int hash_check_args(void * h_ptr,
	const void * h_strkey, unsigned int h_strlen, const char * func)
{
	if (h_ptr == NULL ||
		h_strkey == NULL ||
		h_strlen == 0 ||
		h_strlen >= HASH_INTPTR_MAXLEN) {
		fprintf(stderr, "Error, invalid arguments in [%s]: %p, %p, %d\n",
			func, h_ptr, h_strkey, h_strlen);
		fflush(stderr);
		return HASH_INTPTR_ERROR_ARGS;
	}
	return 0;
}

int hash_intptr_addintptr(struct hash_intptr * * p_hash_intptr,
	const void * p_strkey, unsigned int p_strlen,
	int p_int_value, void * p_ptr_value)
{
	int ret;
	struct hash_intptr * fg_new_hash;
	struct hash_intptr * fg_hash, * fg_old;

	ret = hash_check_args((void *) p_hash_intptr,
		p_strkey, p_strlen, __FUNCTION__);
	if (ret < 0)
		return HASH_INTPTR_ERROR_ARGS;

	fg_new_hash = NULL;
	fg_hash = fg_old = *p_hash_intptr;
	HASH_FIND(hh, fg_hash, p_strkey, p_strlen, fg_new_hash);
	if (fg_new_hash == NULL) {
		fg_new_hash = (struct hash_intptr *) calloc(0x1, sizeof(*fg_new_hash));
		if (fg_new_hash == NULL) {
			fputs("Error, system out of memory!\n", stderr);
			fflush(stderr);
			if (fg_hash != fg_old)
				*p_hash_intptr = fg_hash;
			return HASH_INTPTR_ERROR_MEMORY;
		}

		fg_new_hash->hh_keylen = p_strlen;
		fg_new_hash->hh_int = p_int_value;
		fg_new_hash->hh_ptr = p_ptr_value;
		memcpy(fg_new_hash->hh_key, p_strkey, p_strlen);
		if (p_strlen < HASH_INTPTR_MAXLEN)
			fg_new_hash->hh_key[p_strlen] = (unsigned char) 0;
		HASH_ADD(hh, fg_hash, hh_key, p_strlen, fg_new_hash);
	} else {
		if (fg_new_hash->hh_keylen != p_strlen) {
			fprintf(stderr, "FATAL ERROR, keys: %p, %p, keylen: %u, %u\n",
				fg_new_hash->hh_key, p_strkey, fg_new_hash->hh_keylen, p_strlen);
			fflush(stderr);
		}
		fg_new_hash->hh_int = p_int_value;
		fg_new_hash->hh_ptr = p_ptr_value;
	}

	if (fg_hash != fg_old)
		*p_hash_intptr = fg_hash;
	return HASH_INTPTR_ERROR_OK;
}

int hash_intptr_addint(struct hash_intptr * * a_hash_intptr,
	const void * a_strkey, unsigned int a_strlen, int a_int_value)
{
	return hash_intptr_addintptr(a_hash_intptr,
		a_strkey, a_strlen, a_int_value, NULL);
}

int hash_intptr_addptr(struct hash_intptr * * p_hash_intptr,
	const void * p_strkey, unsigned int p_strlen, void * p_ptr_value)
{
	return hash_intptr_addintptr(p_hash_intptr,
		p_strkey, p_strlen, 0, p_ptr_value);
}

int hash_intptr_findint(struct hash_intptr * f_hash_intptr,
	const void * f_strkey, unsigned int f_strlen, int * f_int_value)
{
	int ret;
	struct hash_intptr * fg_new_hash;

	if (f_hash_intptr == NULL)
		return HASH_INTPTR_ERROR_NOTFOUND;

	ret = hash_check_args((void *) f_hash_intptr,
		f_strkey, f_strlen, __FUNCTION__);
	if (ret < 0)
		return HASH_INTPTR_ERROR_ARGS;

	fg_new_hash = NULL;
	HASH_FIND(hh, f_hash_intptr, f_strkey, f_strlen, fg_new_hash);
	if (fg_new_hash == NULL)
		return HASH_INTPTR_ERROR_NOTFOUND;

	if (f_int_value != NULL)
		*f_int_value = fg_new_hash->hh_int;
	return HASH_INTPTR_ERROR_OK;
}

int hash_intptr_findptr(struct hash_intptr * f_hash_intptr,
	const void * f_strkey, unsigned int f_strlen, void * * f_ptr_value)
{
	int ret;
	struct hash_intptr * fg_new_hash;

	if (f_hash_intptr == NULL)
		return HASH_INTPTR_ERROR_NOTFOUND;

	ret = hash_check_args((void *) f_hash_intptr,
		f_strkey, f_strlen, __FUNCTION__);
	if (ret < 0)
		return HASH_INTPTR_ERROR_ARGS;

	fg_new_hash = NULL;
	HASH_FIND(hh, f_hash_intptr, f_strkey, f_strlen, fg_new_hash);
	if (fg_new_hash == NULL)
		return HASH_INTPTR_ERROR_NOTFOUND;

	if (f_ptr_value != NULL)
		*f_ptr_value = fg_new_hash->hh_ptr;
	return HASH_INTPTR_ERROR_OK;
}

int hash_intptr_remove(struct hash_intptr * * r_hash_intptr,
	const void * r_strkey, unsigned int r_strlen)
{
	int ret;
	struct hash_intptr * fg_old_hash;
	struct hash_intptr * fg_hash, * fg_old;

	ret = hash_check_args((void *) r_hash_intptr,
		r_strkey, r_strlen, __FUNCTION__);
	if (ret < 0)
		return HASH_INTPTR_ERROR_ARGS;

	fg_old_hash = NULL;
	fg_hash = fg_old = *r_hash_intptr;
	if (fg_hash == NULL)
		return HASH_INTPTR_ERROR_OK;

	HASH_FIND(hh, fg_hash, r_strkey, r_strlen, fg_old_hash);
	if (fg_old_hash == NULL)
		return HASH_INTPTR_ERROR_OK;

	HASH_DEL(fg_hash, fg_old_hash);
	fg_old_hash->hh_key[0] = (unsigned char) 0;
	fg_old_hash->hh_keylen = 0;
	fg_old_hash->hh_int = 0;
	fg_old_hash->hh_ptr = NULL;
	free(fg_old_hash);
	fg_old_hash = NULL;

	if (fg_hash != fg_old)
		*r_hash_intptr = fg_hash;
	return HASH_INTPTR_ERROR_OK;
}

int hash_intptr_count(struct hash_intptr * c_hash_intptr)
{
	int rval = 0;
	if (c_hash_intptr != NULL) {
		rval = HASH_COUNT(c_hash_intptr);
	}
	return rval;
}

int hash_intptr_iter(struct hash_intptr * * i_hash_intptr,
	int (* iter_func)(struct hash_intptr *, int, void *), void * whatp)
{
	int rval;
	struct hash_intptr * fg_iter, * fg_tmp;
	struct hash_intptr * fg_hash, * fg_old;

	rval = 0;
	if (i_hash_intptr == NULL)
		return HASH_INTPTR_ERROR_ARGS;
	if (iter_func == NULL)
		return HASH_INTPTR_ERROR_ARGS;

	fg_iter = fg_tmp = NULL;
	fg_hash = fg_old = *i_hash_intptr;
	if (fg_hash == NULL)
		return rval;

	HASH_ITER(hh, fg_hash, fg_iter, fg_tmp) {
		if (iter_func(fg_iter, rval, whatp) < 0)
			break;
		rval++;
	}

	if (fg_old != fg_hash)
		*i_hash_intptr = fg_hash;
	return rval;
}

void hash_intptr_removeall(struct hash_intptr * * ra_hash_intptr)
{
	struct hash_intptr * fg_iter, * fg_tmp;
	struct hash_intptr * fg_hash, * fg_old;

	if (ra_hash_intptr == NULL)
		return;

	fg_iter = fg_tmp = NULL;
	fg_hash = fg_old = *ra_hash_intptr;
	if (fg_hash == NULL)
		return;

	HASH_ITER(hh, fg_hash, fg_iter, fg_tmp) {
		HASH_DEL(fg_hash, fg_iter);

		fg_iter->hh_key[0] = (unsigned char) 0;
		fg_iter->hh_keylen = 0;
		fg_iter->hh_int = 0;
		fg_iter->hh_ptr = NULL;
		free(fg_iter);
	}

	if (fg_hash != fg_old)
		*ra_hash_intptr = fg_hash;
}
