/* aht performance tester:
 *
 * usage: benckmark -f <filename> [-r <ripetitions>] [-s <elements>] [-pdaRUh]:
 *
 * -f <filename>:	specify the key list filename
 * -r <ripetitions>:	number of ripetitions of the test
 * -s <start:stop,incr>:perform the test with the specified number of elements
 * -o [IEiec]:		information to output
 * 				I insertion time
 * 				E insertion time / elements
 * 				i serach time
 * 				e search time / elements
 * 				c collisions
 * -d:			use double hashing
 * -a:			use the alternative hashing function
 * -R:			gerenate a keylist of related keys
 * -U:			generate a keylist of unrelated keys
 * -h:			show usage information
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>

#include <aht.h>

#define HT_MONO		0
#define HT_ALT		1
#define HT_DOUBLE	2

#define OUTPUT_ITIME	1
#define OUTPUT_ETIME	2
#define OUTPUT_ISEARCH	4
#define OUTPUT_ESEARCH	8
#define OUTPUT_COLL	16

/* Prototypes */
void usage(void);
void do_test(int elements, char *filename, int output_flags, int hashtype);
void do_keylist(int type);

int main(int argc, char **argv)
{
	int c, j;
	int operation = 0;
	int start_elements = 10000;
	int stop_elements = 10000;
	int incr = 0;
	int ripetitions = 1;
	int hashtype = HT_MONO;
	int output_flags = 0;
	char *filename = NULL;

	while ((c = getopt(argc, argv, "f:r:s:o:daRUh")) != EOF) {
		switch(c) {
		case 'f':
			filename = optarg;
			break;
		case 'r':
			ripetitions = atoi(optarg);
			break;
		case 's':
			j = sscanf(optarg, "%u:%u,%u", &start_elements,
						&stop_elements,
						&incr);
			if (j < 2)
				stop_elements = start_elements;
			if (j < 3)
				incr = 1;
			break;
		case 'o':
			switch(optarg[0]) {
			case 'I': output_flags |= OUTPUT_ITIME; break;
			case 'E': output_flags |= OUTPUT_ETIME; break;
			case 'i': output_flags |= OUTPUT_ISEARCH; break;
			case 'e': output_flags |= OUTPUT_ESEARCH; break;
			case 'c': output_flags |= OUTPUT_COLL; break;
			default:
				  printf("\nInvalid output selected\n\n");
				  usage();
				  break;
			}
			break;
		case 'd':
			hashtype = HT_DOUBLE;
			break;
		case 'a':
			hashtype = HT_ALT;
			break;
		case 'R':
			operation = 1;
			break;
		case 'U':
			operation = 2;
			break;
		case 'h':
		default:
			usage();
			break;
		}
	}

	switch(operation) {
	case 0:
		while(ripetitions--) {
			for (j = start_elements; j <= stop_elements; j += incr)
				do_test(j, filename, output_flags, hashtype);
		}
		break;
	case 1:
	case 2:
		do_keylist(operation);
		break;
	}
	return 0;
}

void usage(void)
{
	printf(
"AHT benchmark (output ready for gplot)\n"
"usage: benckmark -f <filename> [-r <ripetitions>] [-s <elements>] [-pdaRUh]:\n"
"\n"
"-f <filename>:       specify the key list filename\n"
"-r <ripetitions>:    number of ripetitions of the test\n"
"-s <start>:<end>     perform the test with the specified number of elements\n"
"-o [IEiec]:          information to output\n"
"				I insertion time\n"
"				E insertion time / elements\n"
"				i serach time\n"
"				e search time / elements\n"
"				c collisions\n"
"-d:                  use double hashing\n"
"-a:                  use the alternative hashing function\n"
"-R:                  gerenate a keylist of related keys (not implemented)\n"
"-U:                  generate a keylist of unrelated keys (not implemented)\n"
"-h:                  show usage information\n"
	);
	exit(1);
}

unsigned long long get_usec()
{       
	unsigned long long t;
	struct timeval tv;

	gettimeofday(&tv, NULL);
	t = (tv.tv_sec * 1000000) + tv.tv_usec;
	return t;
}

void do_test(int elements, char *filename, int output_flags, int hashtype)
{
	FILE *fp;
	int i;
	struct hashtable t;
	char key[1024];
	int ret;
	unsigned long long timestamp;
	int index;

	if (filename == NULL) {
		printf("You must specify a filename with the -f switch\n");
		exit(1);
	}

	if ((fp = fopen(filename, "r")) == NULL) {
		perror("fopen: opening keylist");
		exit(1);
	}

	/* initialize the hash table */
	ht_init(&t);
	switch(hashtype) {
	case HT_DOUBLE:
		t.hashf[1] = alt_hashf;
		break;
	case HT_ALT:
		t.hashf[0] = alt_hashf;
		break;
	}

	/* add the keys */
	timestamp = get_usec();
	for (i = 0; i < elements; i++) {
		if (fgets(key, 1024, fp) == NULL)
			break;
		ret = ht_add(&t, key, strlen(key), "somevalue", 9);
		if (ret != HT_OK && ret != HT_BUSY) {
			printf("ht_add error %d\n", ret);
			exit(1);
		}
	}
	timestamp = get_usec() - timestamp;
	if (output_flags & OUTPUT_ITIME) {
		printf("# Microsecond to add %d elements: %Lu\n",
			t.used, timestamp);
		printf("%d %Lu\n", t.used, timestamp);
	}
	if (output_flags & OUTPUT_ETIME) {
		printf("# Microseconds to add one element: %f\n",
			timestamp/(float)t.used);
		printf("%d %f\n", t.used, timestamp/(float)t.used);
	}

	/* search every key one time */
	fseek(fp, 0, SEEK_SET);
	t.collisions = 0;
	timestamp = get_usec();
	for (i = 0; i < elements; i++) {
		if (fgets(key, 1024, fp) == NULL)
			break;
		ret = ht_search(&t, key, strlen(key), NULL, &index);
		if (ret != HT_FOUND) {
			printf("ht_search error %d\n", ret);
			exit(1);
		}
	}
	timestamp = get_usec() - timestamp;
	if (output_flags & OUTPUT_ISEARCH) {
		printf("# Microsecond to search %d elements: %Lu\n",
			t.used, timestamp);
		printf("%d %Lu\n", t.used, timestamp);
	}
	if (output_flags & OUTPUT_ESEARCH) {
		printf("# Microseconds to search one element: %f\n",
			timestamp/(float)t.used);
		printf("%d %f\n", t.used, timestamp/(float)t.used);
	}
	fclose(fp);

	if (output_flags & OUTPUT_COLL) {
		printf("# Collisions: %u\n", t.collisions);
		printf("%d %u\n", t.used, t.collisions);
	}

	/* destroy the hash table */
	timestamp = get_usec();
	ht_destroy(&t);
	timestamp = get_usec() - timestamp;
}

void do_keylist(int type)
{
}
