/*
 * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
 *                         University Research and Technology
 *                         Corporation.  All rights reserved.
 * Copyright (c) 2004-2014 The University of Tennessee and The University
 *                         of Tennessee Research Foundation.  All rights
 *                         reserved.
 * Copyright (c) 2004-2007 High Performance Computing Center Stuttgart,
 *                         University of Stuttgart.  All rights reserved.
 * Copyright (c) 2004-2005 The Regents of the University of California.
 *                         All rights reserved.
 * Copyright (c) 2007      Voltaire All rights reserved.
 * $COPYRIGHT$
 *
 * Additional copyrights may follow
 *
 * $HEADER$
 */

#include "opal_config.h"

#include "opal/class/opal_list.h"
#include "opal/constants.h"

/*
 *  List classes
 */

static void opal_list_item_construct(opal_list_item_t *);
static void opal_list_item_destruct(opal_list_item_t *);

OBJ_CLASS_INSTANCE(opal_list_item_t, opal_object_t, opal_list_item_construct,
                   opal_list_item_destruct);

static void opal_list_construct(opal_list_t *);
static void opal_list_destruct(opal_list_t *);

OBJ_CLASS_INSTANCE(opal_list_t, opal_object_t, opal_list_construct, opal_list_destruct);

/*
 *
 *      opal_list_link_item_t interface
 *
 */

static void opal_list_item_construct(opal_list_item_t *item)
{
    item->opal_list_next = item->opal_list_prev = NULL;
    item->item_free = 1;
#if OPAL_ENABLE_DEBUG
    item->opal_list_item_refcount = 0;
    item->opal_list_item_belong_to = NULL;
#endif
}

static void opal_list_item_destruct(opal_list_item_t *item)
{
#if OPAL_ENABLE_DEBUG
    assert(0 == item->opal_list_item_refcount);
    assert(NULL == item->opal_list_item_belong_to);
#endif /* OPAL_ENABLE_DEBUG */
}

/*
 *
 *      opal_list_list_t interface
 *
 */

static void opal_list_construct(opal_list_t *list)
{
#if OPAL_ENABLE_DEBUG
    /* These refcounts should never be used in assertions because they
       should never be removed from this list, added to another list,
       etc.  So set them to sentinel values. */

    OBJ_CONSTRUCT(&(list->opal_list_sentinel), opal_list_item_t);
    list->opal_list_sentinel.opal_list_item_refcount = 1;
    list->opal_list_sentinel.opal_list_item_belong_to = list;
#endif

    list->opal_list_sentinel.opal_list_next = &list->opal_list_sentinel;
    list->opal_list_sentinel.opal_list_prev = &list->opal_list_sentinel;
    list->opal_list_length = 0;
}

/*
 * Reset all the pointers to be NULL -- do not actually destroy
 * anything.
 */
static void opal_list_destruct(opal_list_t *list)
{
    opal_list_construct(list);
}

static void opal_list_transfer(opal_list_item_t *pos, opal_list_item_t *begin,
                               opal_list_item_t *end)
{
    volatile opal_list_item_t *tmp;

    if (pos != end) {
        /* remove [begin, end) */
        end->opal_list_prev->opal_list_next = pos;
        begin->opal_list_prev->opal_list_next = end;
        pos->opal_list_prev->opal_list_next = begin;

        /* splice into new position before pos */
        tmp = pos->opal_list_prev;
        pos->opal_list_prev = end->opal_list_prev;
        end->opal_list_prev = begin->opal_list_prev;
        begin->opal_list_prev = tmp;
#if OPAL_ENABLE_DEBUG
        {
            volatile opal_list_item_t *item = begin;
            while (pos != item) {
                item->opal_list_item_belong_to = pos->opal_list_item_belong_to;
                item = item->opal_list_next;
                assert(NULL != item);
            }
        }
#endif /* OPAL_ENABLE_DEBUG */
    }
}

void opal_list_join(opal_list_t *thislist, opal_list_item_t *pos, opal_list_t *xlist)
{
    if (0 != opal_list_get_size(xlist)) {
        opal_list_transfer(pos, opal_list_get_first(xlist), opal_list_get_end(xlist));

        /* fix the sizes */
        thislist->opal_list_length += xlist->opal_list_length;
        xlist->opal_list_length = 0;
    }
}

void opal_list_splice(opal_list_t *thislist, opal_list_item_t *pos, opal_list_t *xlist,
                      opal_list_item_t *first, opal_list_item_t *last)
{
    size_t change = 0;
    opal_list_item_t *tmp;

    if (first != last) {
        /* figure out how many things we are going to move (have to do
         * first, since last might be end and then we wouldn't be able
         * to run the loop)
         */
        for (tmp = first; tmp != last; tmp = opal_list_get_next(tmp)) {
            change++;
        }

        opal_list_transfer(pos, first, last);

        /* fix the sizes */
        thislist->opal_list_length += change;
        xlist->opal_list_length -= change;
    }
}

int opal_list_sort(opal_list_t *list, opal_list_item_compare_fn_t compare)
{
    opal_list_item_t *item;
    opal_list_item_t **items;
    size_t i, index = 0;

    if (0 == list->opal_list_length) {
        return OPAL_SUCCESS;
    }
    items = (opal_list_item_t **) malloc(sizeof(opal_list_item_t *) * list->opal_list_length);

    if (NULL == items) {
        return OPAL_ERR_OUT_OF_RESOURCE;
    }

    while (NULL != (item = opal_list_remove_first(list))) {
        items[index++] = item;
    }

    qsort(items, index, sizeof(opal_list_item_t *), (int (*)(const void *, const void *)) compare);
    for (i = 0; i < index; i++) {
        opal_list_append(list, items[i]);
    }
    free(items);
    return OPAL_SUCCESS;
}
