#include <stdlib.h>
#include <memory.h>
#include <assert.h>
#include "utils/nt_std_t.h"

int nt_link_num(nt_link_tp linkp){
	nt_link_tp curp;
	int num = 1;
	assert(linkp);
	assert(linkp->next);
	curp= linkp->next;
	while(curp != linkp){
		num++;
		curp = curp->next;
	}
	return num;
}

void* nt_link_get_by_index(nt_link_tp linkp, int index){
	nt_link_tp curp;
	int i = 0;
	assert(linkp);
	assert(linkp->next);
	if(index == 0)
		return linkp->data;
	curp= linkp->next;
	while(curp != linkp){
		i++;
		if(i == index)
			return curp->data;
		curp = curp->next;
	}
	return NULL;
}

nt_link_tp nt_link_add_data(nt_link_tp link, void *data)
{
	nt_link_tp ptr = malloc(sizeof(nt_link_t));
	if(ptr == NULL)
		return NULL;

	ptr->data = data;
	
	if(link != NULL){
		ptr->prev = link->prev;
		ptr->prev->next = ptr;
		link->prev = ptr;
		ptr->next = link;
	}else{
		ptr->prev = ptr;
		ptr->next = ptr;
	}
	return ptr;
}

nt_link_tp nt_link_remove(nt_link_tp src, nt_link_tp target)
{
	while(src != target){
		if(src->next == target){
			src->next = target->next;
			target->next->prev = src;
			return target;
		}
		src = src->next;
	}
	return target;
}

nt_link_tp nt_link_remove_by_data(nt_link_tp link, void *data)
{
	nt_link_tp ptr = link;
	do{
		if(ptr->data  == data){
			ptr->prev->next = ptr->next;
			ptr->next->prev = ptr->prev;
			return ptr;
		}
		ptr++;
	}while(ptr != link);

	return NULL;
}

void* nt_link_get(nt_link_tp link)
{
	return link->data;
}

nt_link_tp nt_link_find(nt_link_tp link, void *data, 
					nt_compare_tp comp_func)
{
	nt_link_tp ptr = link;
	do{
		if((comp_func)(ptr->data, data)){
			return ptr;
		}
		ptr = ptr->next;
	}while(ptr != link);

	return NULL;

}

nt_link_tp nt_link_next(nt_link_tp link)
{
	return link->next;
}

void nt_all_link_free(nt_link_tp ptr, nt_memfree_tp free_func)
{
	nt_link_tp linkp;
	nt_link_tp tmp_linkp;
	assert(ptr);

	linkp = ptr;
	do{
		if(linkp->data && free_func)
			(free_func)(linkp->data);
		tmp_linkp = linkp;	
		linkp = linkp->next;
		free(tmp_linkp);
	}while(ptr != linkp);
}

nt_key_value_tp nt_key_value_alloc(char *key, char *value)
{
	nt_key_value_tp ptr = malloc(sizeof(nt_key_value_t));
	if(ptr == NULL)
		return NULL;
	ptr->key = key;
	ptr->value = value;
	return ptr;
}

void* nt_stack_add_last(nt_stack_tp stackp, void* data)
{
	int cursor;
	nt_link_tp linkp;

	assert(stackp);
	if(!stackp->linkp){
		return NULL;
	}
	linkp = stackp->linkp;
	cursor = 0;
	do{
		/* If there are data after the cursor, 
		 * then do nothing. 
		 */
		if(cursor > stackp->cursor){
			return NULL;
		}
		linkp = linkp->next;
		cursor++;
	}while(linkp != stackp->linkp);

	linkp = nt_link_add_data(stackp->linkp, data);
	if(!linkp)
		return NULL;
	/* This special case we do not increment cursor */
	/* stackp->cursor++;*/
	return data;
}

void* nt_stack_push(nt_stack_tp stackp, void* data)
{
	int cursor;
	nt_link_tp saved_linkp, linkp;

	assert(stackp);
	if(!stackp->linkp){
		linkp = nt_link_add_data(stackp->linkp, data);
		if(!linkp)
			return NULL;
		stackp->linkp = linkp;
		stackp->cursor = 0;
		return data;
	}
	linkp = stackp->linkp;
	cursor = 0;
	do{
		/* Remove the links after the current corsor. */
		if(cursor > stackp->cursor){
			saved_linkp = stackp->linkp->prev;
			stackp->linkp->prev = linkp->prev;
			linkp->prev->next = stackp->linkp;

			linkp->prev = saved_linkp;
			saved_linkp->next = linkp;

			/* Now the linkp is dangling */
			if(!stackp->unlinkedp){
				stackp->unlinkedp = linkp;
			}else{
				saved_linkp = stackp->unlinkedp->prev;
				stackp->unlinkedp->prev = linkp->prev;
				saved_linkp->next = linkp;
				linkp->prev = saved_linkp;
			}
			break;
		}
		linkp = linkp->next;
		cursor++;
	}while(linkp != stackp->linkp);

	linkp = nt_link_add_data(stackp->linkp, data);
	if(!linkp)
		return NULL;
	stackp->cursor++;
	return data;
}

void* nt_stack_pop(nt_stack_tp stackp)
{
	int cursor;
	nt_link_tp linkp;
	assert(stackp);
	if(!stackp->linkp)
		return NULL;
	linkp = stackp->linkp;
	cursor = 0;
	do{
		if(cursor == stackp->cursor){
			stackp->cursor--;
			if(stackp->cursor < 0){
				stackp->cursor++;
			}
			return linkp->data;
		}
		linkp = linkp->next;
		cursor++;
	}while(linkp != stackp->linkp);
	return NULL;
}

void* nt_stack_cursor_next(nt_stack_tp stackp)
{
	int cursor;
	nt_link_tp linkp;
	assert(stackp);
	if(!stackp->linkp)
		return NULL;
	linkp = stackp->linkp;
	cursor = 0;
	do{
		if(cursor > (stackp->cursor+1)){
			stackp->cursor++;
			return linkp->data;
		}
		linkp = linkp->next;
		cursor++;
	}while(linkp != stackp->linkp);
	return NULL;
}

nt_stack_tp nt_stack_alloc()
{
	nt_stack_tp stackp = malloc(sizeof(nt_stack_t));
	if(!stackp)
		return NULL;

	stackp->cursor = -1;
	stackp->linkp = NULL;
	stackp->unlinkedp = NULL;
	
	return stackp;
}


void nt_stack_free(nt_stack_tp ptr, nt_memfree_tp free_func)
{
	assert(ptr);

	if(!free_func){
		free(ptr);
		return;
	}
	if(ptr->linkp){
		nt_all_link_free(ptr->linkp, free_func);
	}
	if(ptr->unlinkedp){
		nt_all_link_free(ptr->unlinkedp, free_func);
	}
	free(ptr);	
}

