/*****************************************************************************
 * Copyright (c) 2005  Daniel Lerch Hostalot <http://daniellerch.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a 
 * copy of this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the 
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE.
 ****************************************************************************/


#include "hash.h"
#include <stdlib.h>


/* ------------------------------------------------------------------------- */
/* Funcion de hash adecuada para cadenas. */
int hashfunc (void* key, size_t tablesize)
{
	int mult = 31;   /* 31 y 37 consiguen una buena dispersion */
	unsigned int h;
	unsigned char *p;

	h = 0;
	
	for (p=(unsigned char*)key; *p!=0; p++) h = mult*h + *p;

	return h%tablesize;
}

/* ------------------------------------------------------------------------- */
void* hash_new (unsigned int maxsize)
{
	hash_t* obj;
	int i;

	if (maxsize==0) {

		fprintf(stderr, "hash_new(): maxsize debe ser > 0\n");
		exit(EXIT_FAILURE);
	}

	obj = malloc(sizeof(hash_t));
	if(!obj) {

		perror("hash_new()");
		exit(EXIT_FAILURE);
	}

	obj->size = 0;
	obj->maxsize = maxsize;
	obj->hashfunc = hashfunc;
	obj->cmpfunc = NULL;
	obj->table = malloc(maxsize*sizeof(list_t*));

	if (!obj->table) {

		perror("hash_new()");
		exit(EXIT_FAILURE);
	}

	for (i=0; i<maxsize; i++) obj->table[i] = list_new();

	return obj;
}

// {{{ hash_free() -----------------------------------------------------------
void hash_free (hash_t* obj, 
                void (*freefunc_key)(void*), 
                void (*freefunc_value)(void*)) 
{
	int i;
	hash_pair_t* pair;

	for (i=0; i<obj->maxsize; i++) {

		
		list_begin(obj->table[i]);
		while ((list_next(obj->table[i]))) {

			pair = list_remove (obj->table[i], 0);
			if (freefunc_key) freefunc_key(pair->key);
			if (freefunc_value) freefunc_value(pair->value);
			free(pair);
		}

		list_free(obj->table[i], free);
	}

	free(obj->table);
	free(obj);
}
// }}}

/* ------------------------------------------------------------------------- */
void hash_set_hashfunc (hash_t* obj,  int (*hashfunc)(void*, size_t))
{
	obj->hashfunc = hashfunc;
}

/* ------------------------------------------------------------------------- */
void hash_add (hash_t* obj, void* key, void *value)
{
	hash_pair_t* pair;

	pair = malloc(sizeof(hash_t));
	if(!pair) {

		perror("hash_add()");
		exit(EXIT_FAILURE);
	}

	pair->key = key;
	pair->value = value;

	list_add_last (obj->table[obj->hashfunc(key, obj->maxsize)], pair);
}

/* ------------------------------------------------------------------------- */
void* hash_get (hash_t* obj, void *key)
{
	hash_pair_t *pair;
	list_t* l = obj->table[obj->hashfunc(key, obj->maxsize)];

	list_begin(l);
	while ((pair=list_next(l))) {

		if (obj->cmpfunc) {

			if (obj->cmpfunc(key, pair->key)==0) return pair->value;
		}	
		else if (key == pair->key) return pair->value;
	}

	return NULL;
}

/* ------------------------------------------------------------------------- */
void* hash_remove (hash_t* obj, void *key)
{
	hash_pair_t *pair, *tmp;
	void* element;
	int i=0;
	list_t* l = obj->table[obj->hashfunc(key, obj->maxsize)];

	list_begin(l);
	while ((pair=list_next(l))) {
	
		if (obj->cmpfunc) {

			if (obj->cmpfunc(key, pair->key)==0) {
	
				tmp = list_remove (l, i);
				element = tmp->value;
				free(tmp);
				return element;
			}
		}
		else if (key == pair->key) {
		
			tmp = list_remove (l, i);
			element = tmp->value;
			free(tmp);
			return element;
		}
		
		i++;
	}

	return NULL;
}

/* ------------------------------------------------------------------------- */
int hash_contains_key (hash_t* obj, void *key)
{
	if (hash_get (obj, key)) return 1;

	return 0;
}

/* ------------------------------------------------------------------------- */
int hash_size (list_t* obj)
{
	return obj->size;
}


/* ------------------------------------------------------------------------- */
void hash_set_cmpfunc (hash_t* obj, int (*cmpfunc)(void*, void*))
{
	obj->cmpfunc = cmpfunc;
}






