/*
 * Created by jiaqiang.ye@lnxall.com
 *
 * hash function test
 *
 * 2022/05/27
 */

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>

#include "dy_utils/hash_intptr.h"

static int g_rand_fd;
static struct hash_intptr * g_hash_intptr;

static int get_rand_value(void * pval, size_t valsize)
{
	int rfd;
	ssize_t ret;

	rfd = g_rand_fd;
	if (rfd == -1) {
		rfd = open("/dev/urandom", O_RDONLY);
		if (rfd == -1) {
			fprintf(stderr, "Error, failed to open random device: %s\n",
				strerror(errno));
			fflush(stderr);
			return -1;
		}
		g_rand_fd = rfd;
	}

	ret = read(rfd, pval, valsize);
	if (ret != (ssize_t) valsize) {
		fprintf(stderr, "Error, failed to read random device: %s\n",
			strerror(errno));
		fflush(stderr);
		return -1;
	}
	return 0;
}

static char * get_rand_str(size_t strsize)
{
	char * retval;
	size_t idx, jdx;
	unsigned char * randval;
	const char * raw_str = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

	randval = (unsigned char *) malloc(strsize);
	retval = (char *) malloc(strsize + 1);
	if (randval == NULL || retval == NULL) {
		if (randval)
			free(randval);
		if (retval)
			free(retval);
		return NULL;
	}

	jdx = strlen(raw_str);
	get_rand_value((void *) randval, strsize);
	for (idx = 0; idx < strsize; ++idx) {
		size_t rval;
		rval = (size_t) randval[idx];
		rval %= jdx;
		retval[idx] = raw_str[rval];
	}

	free(randval);
	retval[strsize] = '\0';
	return retval;
}

struct rand_value {
	char * key;
	unsigned int keylen;
	int value;
};
#define NUM_RAND_VALUE 16

static void rand_value_free(struct rand_value * rvals, int num)
{
	int idx;
	struct rand_value * rval;

	for (idx = 0; idx < num; ++idx) {
		rval = &rvals[idx];
		if (rval->key != NULL) {
			free(rval->key);
			rval->key = NULL;
		}
	}
	free(rvals);
}

static int dump_hash_element(struct hash_intptr * hint, int number, void * whatp)
{
	fprintf(stdout, "HASH[%d]: %s => %d\n",
		number, (const char *) hint->hh_key, hint->hh_int);
	fflush(stdout);
	return 0;
}

int main(int argc, char *argv[])
{
	int idx, ret;
	struct rand_value * rval;
	struct rand_value * rvals;
	struct hash_intptr * hintptr;
	const char * sepline = "================================================\n";

	/* initialize global hash structure */
	g_rand_fd = -1;
	g_hash_intptr = NULL;

	rvals = (struct rand_value *) calloc(NUM_RAND_VALUE, sizeof(struct rand_value));
	if (rvals == NULL) {
		fputs("Error, system out of memory!\n", stderr);
		fflush(stderr);
		return 1;
	}

	hintptr = NULL;
	for (idx = 0; idx < NUM_RAND_VALUE; ++idx) {
		unsigned int keylen;

		rval = &rvals[idx];
		keylen = 0x1u + (unsigned int) (idx << 1);
		rval->key = get_rand_str((size_t) keylen);
		rval->keylen = (unsigned int) keylen;
		get_rand_value(&rval->value, sizeof(rval->value));
		rval->value &= 0x7FFFFFFF;
		ret = hash_intptr_addint(&g_hash_intptr, rval->key, rval->keylen, rval->value);
		if (ret < 0) {
			fprintf(stderr, "Error, failed to insert hashmap: %s => %d\n",
				rval->key, rval->value);
			fflush(stderr);
			rand_value_free(rvals, NUM_RAND_VALUE);
			return 2;
		}
		if (hintptr != g_hash_intptr) {
			fprintf(stdout, "hash_intptr has changed: %p => %p, idx: %d\n",
				hintptr, g_hash_intptr, idx);
			fflush(stdout);
			hintptr = g_hash_intptr;
		}
	}

	fprintf(stdout, "Number of elements: %d\n", hash_intptr_count(g_hash_intptr));
	fputs(sepline, stdout); fflush(stdout);

	rval = &rvals[NUM_RAND_VALUE / 2];
	ret = hash_intptr_remove(&g_hash_intptr, rval->key, rval->keylen);
	fprintf(stdout, "remove \"%s\" => %d\n", rval->key, ret);
	fprintf(stdout, "Number of elements: %d\n", hash_intptr_count(g_hash_intptr));
	fflush(stdout);
	fputs(sepline, stdout); fflush(stdout);

	hash_intptr_iter(&g_hash_intptr, dump_hash_element, NULL);
	fputs(sepline, stdout); fflush(stdout);

	for (idx = 0; idx < NUM_RAND_VALUE; ++idx) {
		int ival;

		ival = 0;
		rval = &rvals[idx];
		ret = hash_intptr_findint(g_hash_intptr, rval->key, rval->keylen, &ival);
		if (ret < 0) {
			fprintf(stderr, "\nWarning, key not found in hashmap: %s => %d\n\n",
				rval->key, ret);
			fflush(stderr);
			fputs(sepline, stdout); fflush(stdout);
			continue;
		}
		fprintf(stdout, "hashmap[\"%s\"] => %d (delta: %d)\n", rval->key, ival,
			rval->value - ival);
		fflush(stdout);
	}

	rand_value_free(rvals, NUM_RAND_VALUE);
	hash_intptr_removeall(&g_hash_intptr);
	fprintf(stderr, "g_hash_intptr: %p\n", g_hash_intptr);
	fflush(stderr);

	if (g_rand_fd != -1) {
		close(g_rand_fd);
		g_rand_fd = -1;
	}
	return 0;
}
