#include "main.h"
#include "listhash.h"

struct list_head *list_add(struct list_t *list, void *data, size_t size) {
	struct list_head *cur = list->end;
	struct list_head *new = xmalloc(sizeof(struct list_head));
	new->data 	= xmalloc(size);
	new->next 	= NULL;
	if(DEBUG) {fprintf(stderr,"list_add; list:%p, data:%p, size:%d\n",list,data,size);}
	memcpy(new->data,data,size);
	if(cur == NULL) {
		new->prev 	= NULL;
		list->begin 	= new;
		list->end 	= new;
	} else {
		cur->next = new;
		new->prev = cur;
		list->end = new;
	}
	++(list->count);
	if(new->data == NULL) {
		fprintf(stderr,"list_add, emiting list_head with data == NULL\n");
	}
	return new;
}


void *list_del_nf(struct list_t *list, struct list_head *item) {
	struct list_head *cur;
	void *temp;
	if(DEBUG) {fprintf(stderr,"list_del_nf; list:%p, item:%p\n",list,item);}
	
	cur = item->prev;
	if(cur == NULL) {
		list->begin = item->next;
		if(item->next != NULL) {
			item->next->prev = NULL;
		}
	} else {
		cur->next = item->next;
		if(item->next != NULL) {
			item->next->prev = cur;
		}
	}
	if(item == list->end) {
		list->end = item->prev;
	}
	temp = item->data;
	free(item);
	--(list->count);
	if(temp == NULL) {
		fprintf(stderr,"list_del_nf: returning NULL\n");
	}
	return temp;
}

void list_del(struct list_t *list, struct list_head *item) {
	void *p;
	p = list_del_nf(list,item);
	free(p);
}

void list_init(struct list_t *list) {
	list->begin 	= NULL;
	list->end 	= NULL;
	//list->current 	= NULL;
	list->count 	= 0;
}

void list_do(struct list_t *list, void (*function)(void *data)) {
	/*
	 * the old value is a safety net, as the function may change the
	 * list while we are moving through it, e.g. delete
	 */ 
	struct list_head *old = NULL;
	struct list_head *cur = list->begin;
	while(cur != NULL) {
		old = cur;
		cur = cur->next;
		//fprintf(stderr,"list_do: cur:%p\n",cur);
		function(old->data);
	}
}

hash_t hash(void *in, unsigned int length) {
	unsigned int i;
	char *p = (char *)in;
	hash_t ha = 0;
	for(i = 0; i < length; ++i) {
		ha = ha * 37 + (int)*(p+i);
	}
	return (ha % HASHSIZE);
}

void hash_table_init(struct hash_table *t) {
	unsigned int i;
	for(i = 0; i < HASHSIZE; ++i) {
		t->table[i] = NULL;
	}
}

void *hash_find(struct hash_table *tab, hash_t ha, unique_t uni) {
	struct hash_bucket *row;
	void *ret = NULL;
	row = tab->table[ha];
	if(row != NULL) {
		if(row->next == NULL) {
			ret = row->data;
		} else {
			while(row->unique != uni && row != NULL) {
				row = row->next;
			}
			if(row != NULL) {
				ret = row->data;
			}
		}
	}
	if(DEBUG) {fprintf(stderr,"hash_find: ret:%p\n",ret);}
	return ret;
}

void *hash_findu(struct hash_table *tab, unique_t uni) {
	hash_t ha = hash(&uni,sizeof(unique_t));
	return hash_find(tab,ha,uni);
}

void *hash_add_ncp(struct hash_table *tab, unique_t uni, void *data) {
	struct hash_bucket *new;
	struct hash_bucket *cur;
	hash_t ha;
	if(DEBUG) {fprintf(stderr,"hash_add_cnp; tab:%p, uni:%x, data:%p\n",tab,uni,data);}
	
	ha = hash(&uni,sizeof(unique_t));
	if(DEBUG) {fprintf(stderr,"hash_add_cnp; ha: %x\n",ha);}
	
	new = xmalloc(sizeof(struct hash_bucket));
	new->unique = uni;
	new->next = NULL;
	new->data = data;
	
	cur = tab->table[ha];
	if(cur == NULL) {
		tab->table[ha] = new;
	} else {
		while(cur->next != NULL) {
			cur = cur->next;
		}
		cur->next = new;
	}
	return new->data;
}

void *hash_add(struct hash_table *tab, unique_t uni, void *data, size_t datasize) {
	void *cp = xmalloc(datasize);
	memcpy(cp,data,datasize);
	return hash_add_ncp(tab,uni,cp);
}

void hash_del(struct hash_table *tab, hash_t ha, unique_t uni) {
	struct hash_bucket *old = NULL;
	struct hash_bucket *cur = NULL;
	
	if(DEBUG) {fprintf(stderr,"hash_del; tab:%p, ha:%x, uni:%x\n",tab,ha,uni);}
	
	cur = tab->table[ha];
	while(cur != NULL) {
		if(cur->unique == uni) {
			break;
		}
		old = cur;
		cur = cur->next;
	}
	if(cur != NULL) {
		if(old != NULL) {
			old->next = cur->next;
		} else {
			tab->table[ha] = cur->next;
		}
		free(cur->data);
		free(cur);
	}
}

void *hash_add_lose(struct hash_table *tab, unique_t uni, void *data, size_t datasize) {
	struct hash_bucket *new;
	struct hash_bucket *cur;
	hash_t ha;
	if(DEBUG) {fprintf(stderr,"hash_add_lose; tab:%p, uni:%x, data:%p, datasize:%d\n",tab,uni,data,datasize);}
	
	ha = hash(&uni,sizeof(unique_t));
	if(DEBUG) {fprintf(stderr,"hash_add_lose; ha: %x\n",ha);}
	
	
	new = xmalloc(sizeof(struct hash_bucket));
	new->unique = uni;
	new->next = NULL;
	new->data = xmalloc(datasize);
	memcpy(new->data,data,datasize);
	
	cur = tab->table[ha];
	if(cur == NULL) {
		tab->table[ha] = new;
	} else {
		free(tab->table[ha]->data);
		free(tab->table[ha]);
		tab->table[ha] = new;
	}
	return new->data;
}

