/* Editor Settings: expandtabs and use 4 spaces for indentation
* ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: *
* -*- mode: c, c-basic-offset: 4 -*- */

/*
 * Copyright Likewise Software    2004-2008
 * All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the license, or (at
 * your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
 * General Public License for more details.  You should have received a copy
 * of the GNU Lesser General Public License along with this program.  If
 * not, see <http://www.gnu.org/licenses/>.
 *
 * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING
 * TERMS AS WELL.  IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT
 * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE
 * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU
 * LESSER GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE.  IF YOU
 * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING
 * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT
 * license@likewisesoftware.com
 */

/**
 * @file list.h
 * @brief List APIs
 */

#ifndef __LWLIST_H__
#define __LWLIST_H__

#include <lw/types.h>
#include <lw/attrs.h>

/**
 * @defgroup Embedded Linked Lists
 * @brief Embedded Linked Lists API
 *
 * This API lets you manipulate embedded linked lists.  These
 * are linked lists where the links are stored inside the object,
 * thus allowing efficient (and simple) manipulation of lists
 * containing the object.  (For example, you can remove an object
 * from a list only by using a reference to the object since the
 * object conains the list pointers and w/o doing any searching.
 * Another example is that you never need to allocate extra
 * memory when adding an element to a list.)
 *
 * @author Danilo Almeida (dalmeida@likewise.com)
 */

/*@{*/

/**
 * @brief Doubly-linked list structure
 *
 * An entry in an embedded linked list.
 */
typedef struct _LW_LIST_ENTRY {
    /** @brief Next entry */
    struct _LW_LIST_ENTRY* Next;
    /** @brief Previous entry */
    struct _LW_LIST_ENTRY* Prev;
} LW_LIST_ENTRY, *PLW_LIST_ENTRY;

#ifndef DOXYGEN
// Obsolete alias for LW_LIST_ENTRY
typedef struct _LW_LIST_ENTRY LW_LIST_LINKS, *PLW_LIST_LINKS;
#endif

/**
 * @brief Initialize list entry
 *
 * Initializes a list entry structure
 *
 * @param[in] Head The list entry.
 */
LW_VOID
LwListInit(
    LW_OUT LW_LIST_ENTRY* Head
    );

/**
 * @brief Test if list is empty
 *
 * Tests if a list is empty.
 *
 * @param[in] Head The list.
 * @retval TRUE The list is empty.
 * @retval FALSE The list is non-empty.
 */
LW_BOOLEAN
LwListIsEmpty(
    LW_IN LW_LIST_ENTRY* Head
    );

/**
 * @brief Insert list element after head
 *
 * Inserts a new list element after the given
 * head element.
 *
 * @param[in] Head The insertion point.
 * @param[in] Element The element to insert.
 */
LW_VOID
LwListInsertAfter(
    LW_IN LW_LIST_ENTRY* Head,
    LW_IN LW_LIST_ENTRY* Element
    );

/**
 * @brief Insert list element before head
 *
 * Inserts a new list element before the given
 * head element.
 *
 * @param[in] Head The insertion point.
 * @param[in] Element The element to insert.
 */
LW_VOID
LwListInsertBefore(
    LW_IN LW_LIST_ENTRY* Head,
    LW_IN LW_LIST_ENTRY* Element
    );

/**
 * @brief Remove element from list.
 *
 * Removes an element from its current list, if any.
 *
 * @param[in] Element The element to remove.
 */
LW_VOID
LwListRemove(
    LW_IN LW_LIST_ENTRY* Element
    );

/**
 * @brief Remove element after head.
 *
 * Removes the element after the given list head, if any.
 *
 * @param[in] Head The list head.
 * @return The removed element
 * @retval NULL The list was empty and no element was removed.
 */
LW_LIST_ENTRY*
LwListRemoveAfter(
    LW_IN LW_LIST_ENTRY* Head
    );

/**
 * @brief Remove element before head.
 *
 * Removes the element before the given list head, if any.
 *
 * @param[in] Head The list head.
 * @return The removed element
 * @retval NULL The list was empty and no element was removed.
 */
LW_LIST_ENTRY*
LwListRemoveBefore(
    LW_IN LW_LIST_ENTRY* Head
    );

/**
 * @brief Move a list head to a new head
 *
 * Relink a list anchored by Old Head to be anchored by New Head. Old Head is
 * empty upon return.
 *
 * @param[in] Old Head.
 * @param[in] new Head.
 * @retval void
 */
LW_VOID
LwListMoveHead(
    LW_IN LW_LIST_ENTRY* oldHead,
    LW_IN LW_LIST_ENTRY* newHead
    );

/**
 * @brief Traverse linked list from head to tail.
 *
 * This function allows a linked list to be conveniently traversed
 * with a simple @a while loop as follows:
 * @code
 * Cursor = NULL;
 * while ((Cursor = LwListTraverse(Head, Cursor)))
 * {
 *    ...
 * }
 * @endcode
 *
 * @param[in] Head The list head.
 * @param[in] Cursor The current list element, or @a NULL if just starting
 * @return The next list element.
 * @retval NULL There are no more elements to visit.
 */
PLW_LIST_ENTRY
LwListTraverse(
    LW_IN PLW_LIST_ENTRY Head,
    LW_IN PLW_LIST_ENTRY Cursor
    );

/**
 * @brief Traverse linked list from tail to head.
 *
 * This function allows a linked list to be conveniently traversed
 * with a simple @a while loop as follows:
 * @code
 * Cursor = NULL;
 * while ((Cursor = LwListTraverseReverse(Head, Cursor)))
 * {
 *    ...
 * }
 * @endcode
 *
 * @param[in] Head The list head.
 * @param[in] Cursor The current list element, or @a NULL if just starting
 * @return The previous list element.
 * @retval NULL There are no more elements to visit.
 */
PLW_LIST_ENTRY
LwListTraverseReverse(
    LW_IN PLW_LIST_ENTRY Head,
    LW_IN PLW_LIST_ENTRY Cursor
    );

/**
 * @brief Test element is in a list.
 *
 * @param[in] Element.
 * @retval TRUE The element is in a list.
 * @retval FALSE The element is not in a list.
 *
 * @note Requires that client of this API initializes all
 *       elements using LwListInit or LW_LIST_INITIALIZER(var)
 *       so this method works on elements that have never been
 *       inserted into a list.
 */
static inline
LW_BOOLEAN
LwListElementIsLinked(
    LW_IN LW_LIST_ENTRY* Element
    )
{
    return (Element->Next != Element);
}

/**
 * @brief List initializer
 *
 * This macro evaluates to a suitable initializer constant
 * for an #LW_LIST_ENTRY variable.
 * @param[in] var The variable being initialized.
 * @hideinitializer
 */
#define LW_LIST_INITIALIZER(var) {&var,&var}
/**
 * @brief Insert element at start of list
 *
 * An alias for #LwListInsertAfter
 * @hideinitializer
 */
#define LwListInsertHead LwListInsertAfter
/**
 * @brief Insert element at end of list
 *
 * An alias for #LwListInsertBefore
 * @hideinitializer
 */
#define LwListInsertTail LwListInsertBefore
/**
 * @brief Remove element at start of list
 *
 * An alias for #LwListRemoveAfter
 * @hideinitializer
 */
#define LwListRemoveHead LwListRemoveAfter
/**
 * @brief Remove element at end of list
 *
 * An alias for #LwListRemoveBefore
 * @hideinitializer
 */
#define LwListRemoveTail LwListRemoveBefore

/*@}*/

#endif /* __LWLIST_H__ */

/*
local variables:
mode: c
c-basic-offset: 4
indent-tabs-mode: nil
tab-width: 4
end:
*/
