/**
* A simple circular linked list implementation used by CSM functions
*
* @author 	: Asanga Udugama
* @email 	: adu@comnets.uni-bremen.de
* @data 	: 07-jun-2005
*
*/

#include "csm_linkedlist.h"

/**
* Initialize linked list head. The function returns a ll_entry_t type data item
* when initialized.
*
* @return ll_entry_t *		- pointer to the newly created linked list head
*
*/
ll_entry_t *ll_init() 
{
	ll_entry_t *ll_head;

	ll_head = (ll_entry_t *) malloc(sizeof(ll_entry_t));
	if(ll_head == NULL) {
		return NULL;
	}
 
	ll_head->prev = ll_head;
	ll_head->next = ll_head;

	ll_head->data = (char *) NULL;

	ll_head->count = 0;
	ll_head->read_next = (ll_entry_t *) NULL;

	return ll_head;
}

/**
* Insert an entry to the linked list at the tail of the list.
*
* @param ll_entry_t *ll_head 	- Pointer to the linked list head
* @param char *data 		- Pointer to the data idem to be placed in the
*				  list
* @return char *		- Pointer to the data
*/
char *ll_add_at_tail(ll_entry_t *ll_head, char *data) 
{
	ll_entry_t *new_entry;

	new_entry = (ll_entry_t *) malloc(sizeof(ll_entry_t));
	if(new_entry == NULL) {
		return NULL;
	}

	new_entry->prev = ll_head->prev;
	new_entry->next = ll_head;
	ll_head->prev->next = new_entry; 
	ll_head->prev = new_entry; 

	new_entry->data = data;
	new_entry->count = NULL;

	ll_head->count++;
	ll_head->read_next = (ll_entry_t *) NULL;

	return data;
}

/**
* Remove an entry of the linked list from the head of the list.
*
* @param ll_entry_t *ll_head 	- Pointer to the linked list head
* @return char *		- Pointer to the data of the removed entry
*/
char *ll_del_from_head(ll_entry_t *ll_head) 
{
	ll_entry_t *del_entry;
	char *data;

	if(ll_head->count <= 0) {
		return NULL;
	}

	del_entry = ll_head->next;
	data = del_entry->data;

	del_entry->next->prev = ll_head;
	ll_head->next = del_entry->next; 

	ll_head->count--;
	ll_head->read_next = (ll_entry_t *) NULL;

	free(del_entry);	

	return data;
}

/**
* Read the next entry in the linked list. Before using the linked list, the
* caller has to initialize (NULL) the read_next entry in the list head. This 
* function is used to read a linked list from head to tail. When the read next 
* reaches the tail, a NULL is returned.
*
* @param ll_entry_t *ll_head 	- Pointer to the linked list head
* @return char *		- Pointer to the data of the next entry
*/
char *ll_get_next(ll_entry_t *ll_head) 
{
	ll_entry_t *read_next;

	if(ll_head->count <= 0) {
		return (char *) NULL;
	}

	if(ll_head->read_next == (ll_entry_t *) NULL) {
		ll_head->read_next = ll_head->next; 
		return ll_head->read_next->data;
	}

	if(ll_head->read_next->next == ll_head) {
		ll_head->read_next = (ll_entry_t *) NULL;
		return NULL;
	}

	read_next = ll_head->read_next->next;
	ll_head->read_next = read_next;
	return read_next->data;
}

/**
* Searches the list to find whether a given value exist. The caller is required
* to provide a compare function to perform the comparison in the appropriate
* manner.
*
* @param ll_entry_t *ll_head 	- Pointer to the linked list head
* @param char *search_item	- Pointer to the search item (as a char *)
* @param char *cmp_func(char *, char *)	- Function that implements the 
*				           comparison
* @return char *		- Pointer to the data if found, or else NULL
*/
char *ll_search(ll_entry_t *ll_head, char *search_item, 
					char *(*cmp_func)(char *, char *)) 
{
	ll_entry_t *read_next;
	char *data;

	if(ll_head->count <= 0) {
		return (char *) NULL;
	}

	read_next = ll_head->next;
	while(read_next != ll_head) {
		data = cmp_func(search_item, read_next->data);
		if(data != NULL) {
			return data;
		}
		read_next = read_next->next;
	}

	return NULL;
}

/**
* Remove the list entry given the pointer to the data item in the list.
*
* @param ll_entry_t *ll_head 	- Pointer to the linked list head
* @param char *data		- Pointer to the data item
* @return char *		- Pointer to the data of the removed entry, 
*				  or NULL if no such entry
*/
char *ll_del_given(ll_entry_t *ll_head, char *data) 
{
	ll_entry_t *read_next;
	ll_entry_t *del_entry;
	int found;

	if(ll_head->count <= 0) {
		return (char *) NULL;
	}

	found = FALSE;
	read_next = ll_head->next;
	while(read_next != ll_head) {
		if(read_next->data == data) {
			del_entry = read_next;
			found = TRUE;
			break;
		}
		read_next = read_next->next;
	}
	
	if(found == TRUE) {
		data = del_entry->data;

		del_entry->next->prev = del_entry->prev;
		del_entry->prev->next = del_entry->next;

		ll_head->count--;
		ll_head->read_next = (ll_entry_t *) NULL;

		free(del_entry);	
		return data;
	}

	return NULL;
}

/**
* Removes the list from memory by removeing all entries in the list and
* then removing the list header itself
*
* @param ll_entry_t *ll_head 	- Pointer to the linked list head
*/
void ll_term(ll_entry_t *ll_head) 
{
	char *data;

	while(ll_head->count > 0) {
		data = ll_del_from_head(ll_head);

		free(data);
	}

	free(ll_head);

	return;
}

