/**
 * \file
 * \brief Barrelfish collections library list data structure
 */
/*
 * Copyright (c) 2010, 2012, ETH Zurich.
 * All rights reserved.
 *
 * This file is distributed under the terms in the attached LICENSE file.
 * If you do not find this file, copies can be found by writing to:
 * ETH Zurich D-INFK, CAB F.78, Universitaetstrasse 6, CH-8092 Zurich,
 * Attn: Systems Group.
 */

#include <collections/list.h>

/******************************************************
 * a simple linked list implementation.
 ******************************************************/

/*
 * private functions.
 */

static void list_push(collections_listnode *existing,
                      collections_listnode *insert)
{
    insert->next = existing->next;
    insert->prev = existing;
    existing->next = insert;
    insert->next->prev = insert;
}

static void *list_pop(collections_listnode *n)
{
    void *data = n->data;
    n->next->prev = n->prev;
    n->prev->next = n->next;
    n->next = n->prev = NULL;
    return data;
}

/*
 * Insert the data at the head.
 */
static collections_listnode *list_create_node(collections_listnode *start,
                                              collections_listnode *where,
                                              void *data)
{
    collections_listnode *newnode = (collections_listnode *)
                                    malloc(sizeof(collections_listnode));
    newnode->data = data;

    list_push(where, newnode);
    ((collections_header_data *)(start->data))->size ++;
    return newnode;
}

static void list_destroy_node(collections_listnode *start,
                              collections_listnode *node)
{
    list_pop(node);
    free(node);
    ((collections_header_data*)start->data)->size--;
}


/*
 * public functions.
 */

/*
 * Creates a new linked list.
 */
void collections_list_create(collections_listnode **start,
                             collections_release_data func)
{
    collections_listnode *t;

	//
	// this is an empty list containing only the header.
	//
    t = (collections_listnode *)malloc(sizeof(collections_listnode));
    t->next = t->prev = t;
    collections_header_data *h = (collections_header_data *)
                                 malloc(sizeof(collections_header_data));
    h->size = 0;
	h->data_free = func;
	h->cur_item = NULL;
    t->data = (void *)h;

    *start = t;
}

/*
 * Releases all the nodes in the list.
 */
void collections_list_release(collections_listnode *start)
{
    collections_release_data data_free =
        ((collections_header_data*)start->data)->data_free;
    collections_listnode *cur = start->next;

    //
    // travel through the rest of the
    // list and release all the nodes.
    //
    while (cur != start)
    {
        void *data = cur->data;
        if (data != NULL && data_free)
        {
            data_free(data);
        }
        list_destroy_node(start, cur);
        cur = start->next;
    }

    //
    // release the header.
    //
    free(start->data);
    free(start);

    return;
}

/*
 * Inserts an element in the head of the list.
 */
int32_t collections_list_insert(collections_listnode *start, void *data)
{
    collections_listnode *ret = list_create_node(start, start, data);
    if (ret) {
        // success ...
        return 0;
    }
    return 1;
}

/*
 * Inserts an element at the tail of the list.
 */
int32_t collections_list_insert_tail(collections_listnode *start, void *data)
{
    collections_listnode *ret = list_create_node(start, start->prev, data);
    if (ret) {
        // success ...
        return 0;
    }
    return 1;
}

/*
 * Returns the data that matches the user provided key.
 */
void *collections_list_find_if(collections_listnode *start,
                               collections_list_predicate p, void *arg)
{
    collections_listnode *cur = start->next;
    while (cur != start)
    {
        if (p(cur->data, arg))
        {
            return cur->data;
        }

        cur = cur->next;
    }
    return NULL;
}

/**
 * Remove the first item that matches the given predicate and return it.
 *
 * \return The removed item.
 */
void *collections_list_remove_if(collections_listnode *start,
                                 collections_list_predicate p, void *arg)
{
    collections_listnode *cur = start->next;
    while (cur != start)
    {
        if (p(cur->data, arg))
        {
            void *data = cur->data;
            list_destroy_node(start, cur);
            return data;
        }
        cur = cur->next;
    }
    return NULL;
}

/**
 * Remove all the items that match the given predicate.
 *
 * \return The number of items removed.
 */
uint32_t collections_list_remove_if_all(collections_listnode *start,
                                    collections_list_predicate p, void *arg)
{
    uint32_t items_removed = 0;

    collections_listnode *cur = start->next;
    while (cur != start)
    {
        if (p(cur->data, arg))
        {
            list_destroy_node(start, cur);
            items_removed++;
        }
        cur = cur->next;
    }

    return items_removed;
}

void *collections_list_remove_ith_item(collections_listnode *start,
                                       uint32_t item)
{
    void *element;

    uint32_t n = collections_list_size(start);
    if (item >= n) {
        element = NULL;
    } else if (item <= n/2) {

        collections_listnode *cur = start->next;
        while (item != 0) {
            cur = cur->next;
            item--;
        }
        element = cur->data;
        list_destroy_node(start, cur);

    } else {

        collections_listnode *cur = start;
        do {
            cur = cur ->prev;
            item++;
        } while (item !=n);
        element = cur->data;
        list_destroy_node(start, cur);
    }

    return element;
}

void *collections_list_get_ith_item(collections_listnode *start, uint32_t item)
{
    uint32_t n = collections_list_size(start);
    if (item >= n) {
        return NULL;
    }
    else if (item <= n / 2)
    {
        collections_listnode* cur = start->next;
        while (item != 0)
        {
            cur = cur->next;
            item--;
        }
        return cur->data;
    }
    else
    {
        collections_listnode *cur = start;
        do {
            cur = cur->prev;
            item++;
        } while (item != n);
        return cur->data;
    }
}

/*
 * Return the total number of nodes in the list.
 */
uint32_t collections_list_size(collections_listnode *start)
{
    return ((collections_header_data *)(start->data))->size;
}

#if 0
static void* list_front(collections_listnode* start)
{
    return (start->next == start) ? NULL : start->next->data;
}

static void* list_back(collections_listnode* start)
{
    return (start->prev == start) ? NULL : start->prev->data;
}
#endif

/*
 * A set of routines for iteratively traversing through
 * the linked list.
 */

/*
 * Opens the list for traversal.
 */
int32_t	collections_list_traverse_start(collections_listnode *start)
{
    collections_header_data* head = (collections_header_data *) start->data;

    if (head->cur_item) {
        // if the next item is not null, a
        // traversal is already in progress.
        printf("Error: list is already opened for traversal.\n");
        return -1;
    }

    head->cur_item = start;

    return 1;
}

/*
 * Fetches the next item in the list.
 * If all the items have been fetched,
 * returns null.
 */
void *collections_list_traverse_next(collections_listnode *start)
{
    collections_header_data *head = (collections_header_data *) start->data;

    if (!head->cur_item) {
        // asking for the next item without
        // starting the traversal.
        printf("Error: list must be opened for traversal.\n");
        return NULL;
    }

    head->cur_item = head->cur_item->next;
    if (head->cur_item == start)
    {
        return NULL;
    }
    else
    {
        return head->cur_item->data;
    }
}

/*
 * Finishes the list traversal.
 */
int32_t	collections_list_traverse_end(collections_listnode *start)
{
    collections_header_data *head = (collections_header_data *) start->data;

    if (!head->cur_item) {
        // closing without
        // starting the traversal.
        printf("Error: list must be opened before ending.\n");
        return -1;
    }

    head->cur_item = NULL;

    return 1;
}

int collections_list_visit(collections_listnode *start,
                           collections_list_visitor_func func, void *arg)
{
    collections_listnode *cur = start->next;
    while (cur != start && func(cur->data, arg))
    {
        cur = cur->next;
    }
    return cur == start;
}
