/* 
 * Copyright (C) 2000-2001 Computer & Communications Research Laboratories,
 *			   Industrial Technology Research Institute
 */
/* Copyright Telcordia Technologies 1999 */
/*
 * adtHash.c
 *
 * $Id: adtHash.c,v 1.7 2001/06/15 02:39:19 sjtsai Exp $
 */

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

#include "adtHash.h"
#include "utils.h"

TCR	hash_tracer = NULL;

typedef struct EntryObj *Entry;
struct EntryObj
{
    char* key;
    char* val;
    Entry next;

};

struct adtHashObj
{
	Entry*		table;
	int		CELLS, elements;
	int		keysUpper, strings;
	Entry		iterNext;	      /* next entry in current bucket */
	int		bucketNext;	      /* next bucket to search */

	TRACE_FLAG	traceFlag;
};

/* create new hashtable
 * initSize = number of entries in table (may grow)
 * keysUpper: bool should keys be mapped to upper case
 * strings: adtHashType   should val be copied (cstrings and ints)
 *                 by adtHashAdd
 * keys are always strings and always copied
 */ 
adtHash adtHashNew(int _initSize, int _keysUpper, adtHashType _strings)
{
	int i;
	
	adtHash _this;
	_this = malloc(sizeof *_this);
	/* ErrorCheck */
	
	_this->CELLS = _initSize;
	_this->table = calloc(_initSize, sizeof(void*));
	/* ErrorCheck */
	
	_this->elements = 0;
	_this->keysUpper = _keysUpper;
	_this->strings = _strings;
	
	for( i=0; i<_this->CELLS; i++ ){
		_this->table[i] = NULL;
	}
	return _this;
}

/* destroy hashtable, call freeVal on each val */
void adtHashFree(adtHash _this, void (*freeVal)(void*))
{
	int i;
	if( _this==NULL ) return;
	
	for( i=0; i<_this->CELLS; i++ ){
		Entry p;
	
		for(p=_this->table[i]; p != NULL ; ) {
			Entry t;
	    
			free(p->key);
			if( freeVal!=NULL ) (*freeVal)(p->val);
			t=p;
			p=t->next;
			free(t);
		}
	}
	
	free(_this->table);
	free(_this);
}

static unsigned int hashCode(adtHash _this, char* str)
{
	unsigned int i = 0;
	while( *str != 0 ) {
		i <<= 1;
		i += (_this->keysUpper ? toupper(*str) : *str);
		str++;
	}
	return i % _this->CELLS;
}

static Entry lookUp(adtHash _this, char* str, Entry bucket, Entry* prev)
{
	*prev = NULL;
	while( bucket != NULL ) {
	if( (_this->keysUpper
	     ? strICmp(bucket->key, str)
	     : strcmp(bucket->key, str)
	    )== 0 ) {
	    return bucket;
	}
	*prev  = bucket;
	bucket = bucket->next;
	}
	return NULL;
}

/* change string in place to upper case */
static void upcaseKey(char* s)
{
	while( *s ) {
		*s = toupper(*s);
		s++;
	}
}

/* add pair key/val to hash, return old value */ 
void*	adtHashAdd(adtHash _this, char* _key, void* _val)
{
	int h = hashCode(_this, _key);
	Entry prev, e =NULL;
	void* oldVal;
	e = lookUp(_this, _key, _this->table[h], &prev);
	
	if( e != NULL ) {
		oldVal = e->val;
	} else {
		oldVal = NULL;
	
		/* create new entry */
		_this->elements++;
		e = malloc( sizeof *e );
		e->key = (char *) strDup(_key);
		if( _this->keysUpper ) upcaseKey(e->key);
	
		/* link into bucket */
		e->next = _this->table[h];
		_this->table[h] = e;
	}
	
	if( _val == NULL ) {
		e->val = NULL;
	} else {
		switch( _this->strings ) {
		case HASH_POINTER:
			e->val = _val; 
			break;
		case HASH_CSTRING:
			if (_val != NULL)
				e->val = (char *) strDup(_val);
			break;
		case HASH_INT:
			e->val = malloc(sizeof(int));
			*(int*)(e->val) = *(int*) _val;
			break;
		default:
			fprintf(stderr, "bad value to adtHashAdd");
		}
	}
	return oldVal;
}
	
/* return val for key in hash.  NULL if not found. */
void*	adtHashItem(adtHash _this, char* _key)
{
	int h = hashCode(_this, _key);
	Entry prev;
	Entry e = lookUp(_this, _key, _this->table[h], &prev);
	if( e == NULL ) return NULL;
	
	return e->val;
}

/* del key from hash, return old value */
void*	adtHashDel(adtHash _this, char* _key)
{
	int h = hashCode(_this, _key);
	Entry prev;
	Entry e = lookUp(_this, _key, _this->table[h], &prev);
	void* oldVal;
	if( e == NULL ) return NULL;
	
	_this->elements--;
	oldVal = e->val;
	
	if( prev==NULL ) 
		_this->table[h] = e->next;
	else             
		prev->next = e->next;
	
	free(e->key);
	free(e);
	
	return oldVal;
}

/* iterator over keys in hash.  NextKeys returns NULL when no keys left.
 * Note that you cannot nest iterations over the same hashTable 
 */
void	StartKeys(adtHash _this)
{
	_this->iterNext = NULL;
	_this->bucketNext = 0;
}

char*	NextKeys(adtHash _this)
{
	while( _this->iterNext == NULL && _this->bucketNext < _this->CELLS ) {
		_this->iterNext = _this->table[_this->bucketNext];
		_this->bucketNext ++;
	}
	
	if( _this->iterNext != NULL ) {
		Entry e = _this->iterNext;
		_this->iterNext = e->next;
		return e->key;
	} else {
		return NULL;
	}
}

int adtHashSize(adtHash _this) {
	return _this->elements;
}

/*========================================================================
// set this adtHash's trace flag ON or OFF*/
void	adtHashSetTraceFlag(adtHash _this,TRACE_FLAG traceflag)
{
	if ( _this==NULL ) 
		return;
	_this->traceFlag = traceflag;
}

/*========================================================================
// vector Tracer */
void		adtHashSetTracer(TCR tracer)
{
	hash_tracer = tracer;
}

TCR		adtHashGetTracer(void)
{
	return hash_tracer;
}



