#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include "speciallist.h"

static speciallist_node_t* node_new(){
	speciallist_node_t *node;

	node = malloc(sizeof(speciallist_node_t));
	node->data = NULL;
	node->next = NULL;
	return node;
}

speciallist_t* speciallist_new (){
	speciallist_t *list = malloc (sizeof(speciallist_t));
	pthread_mutex_init (&list->mutex, NULL);
	list->first = NULL;
	list->last = NULL;
	list->latest = NULL;
	return list;
}

void speciallist_destroy (speciallist_t *list){
	speciallist_node_t *last = list->first;
	speciallist_node_t *next;

	speciallist_lock(list);
	while (last){
		next = last->next;
		free(last);
		last = next;
	}
	speciallist_unlock(list);

	free(list);
	list = NULL;
}

void speciallist_lock (speciallist_t *list){
	pthread_mutex_lock(&list->mutex);
}

void speciallist_unlock	(speciallist_t *list){
	pthread_mutex_unlock(&list->mutex);
}

int speciallist_count (speciallist_t *list){
	int result;

	speciallist_lock(list);
	result = speciallist_count_nolock(list);
        speciallist_unlock(list);

        return result;
}

int speciallist_append (speciallist_t *list, void *data){
	int result;

	speciallist_lock(list);
	result = speciallist_append_nolock(list,data);
	speciallist_unlock(list);

	return result;
}

int speciallist_delete (speciallist_t *list, void *data){
	int result;

	speciallist_lock(list);
	result = speciallist_delete_nolock(list,data);
	speciallist_unlock(list);

	return result;
}

void* speciallist_get_first (speciallist_t *list){
        return speciallist_get_first_nolock (list);
}

void* speciallist_get_last (speciallist_t *list){
	void *result;

	speciallist_lock(list);
        result =  speciallist_get_last_nolock (list);
	speciallist_unlock(list);

	return result;
}

void* speciallist_get_next (speciallist_t *list, void *data){
        void *result;

        speciallist_lock(list);
        result = speciallist_get_next_nolock (list, data);
        speciallist_unlock(list);

        return result;
}

void* speciallist_get_previous (speciallist_t *list, void *data){
        void *result;

        speciallist_lock(list);
        result = speciallist_get_previous_nolock (list, data);
        speciallist_unlock(list);

        return result;
}

int speciallist_count_nolock (speciallist_t *list){
	int i = 0;
	speciallist_node_t *ptr = list->first;

	while (ptr){
		i++;
		ptr = ptr->next;
	}
	return i;
}

int speciallist_append_nolock (speciallist_t *list, void *data){
        speciallist_node_t *ptr = list->first;
        speciallist_node_t *new = node_new();
        new->data = data;

        if (ptr){
		list->last->next = new;
		list->last = list->last->next;
/*
                while (ptr->next) ptr = ptr->next;
                ptr->next = new;
*/
                return 1;
        }
	list->latest = new;
        list->first = new;
	list->last = new;
        return 1;
}

int speciallist_delete_nolock (speciallist_t *list, void *data){
	speciallist_node_t *found;
	speciallist_node_t *last;

	if (!list) return 0;
	if (!list->first || !data) return 0;

	if (list->first->data == data){
		found = list->first;
		if (list->last == list->first)
			list->last = NULL;
		if (list->latest == list->first)
			list->latest = list->first->next;
		list->first = list->first->next;
		free(found);
		return 1;
	}

	last = list->first;
	found = list->first->next;		
	while (found){
		if (found->data == data){
			if (found == list->last)
				list->last = last;
			if (found == list->latest)
				list->latest = list->first;
			last->next = found->next;
			free(found);
			return 1;
		}
		last = found;
		found = found->next;
	}
	return 0;
}

void* speciallist_get_first_nolock (speciallist_t *list){
        if (!list->first) return NULL;

        return list->first->data;
}

void* speciallist_get_last_nolock (speciallist_t *list){
/*
	speciallist_node_t *ptr;
	speciallist_node_t *rptr = NULL;

	ptr = list->first;
	while(ptr){
		rptr = ptr;
		ptr = ptr->next;
	}
	if (rptr) return rptr->data;
	else return NULL;
*/
	return list->last->data;
}

void* speciallist_get_next_nolock (speciallist_t *list, void *data){
	speciallist_node_t *ptr;

	if (!list) return NULL;
	if (!data) return NULL;
	if (!list->first) return NULL;
	if (list->first == list->last) return NULL; /* only one member -> no next */

	if (!list->latest) {printf ("no list->latest\n"); return NULL;} /* this should not happen */

	if (list->latest->data != data)
		list->latest = list->first;

	ptr = list->latest;
	while (ptr){
		if (ptr->data == data) {
                        if (ptr->next){
                                list->latest = ptr->next;
                                return ptr->next->data;
                        } else {
				list->latest = list->first;
				return NULL;
			}
                }
                ptr = ptr->next;
	}

/*
	ptr = list->first;
	while(ptr){
		if (ptr->data == data) {
			if (ptr->next){
				list->latest = ptr->next;
				return ptr->next->data;
			}
			else return list->latest = NULL;
		}
		ptr = ptr->next;
	}		
*/
	return NULL;
}

void* speciallist_get_previous_nolock (speciallist_t *list, void *data){
        speciallist_node_t *ptr;
	speciallist_node_t *rptr = NULL;

	if (!list) return NULL;
        if (!list->first || !data) return NULL;

        ptr = list->first;
        while(ptr){
                if (ptr->data == data) {
			if (rptr){
				list->latest = rptr;
			 	return rptr->data;
        		} else {
				list->latest = list->first;
				return NULL;
			}
                }
		rptr = ptr;
                ptr = ptr->next;
        }
        return NULL;
}

