/*
 * Intel(R) Enclosure LED Utilities
 * Copyright (C) 2009-2018 Intel Corporation.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */

#ifndef _LIST_H_INCLUDED_
#define _LIST_H_INCLUDED_

#include <stdlib.h>

struct node {
	struct node *next, *prev;
	struct list *list;
	void *item;
};

typedef void (*item_free_t)(void *);

struct list {
	struct node *head, *tail;
	item_free_t item_free;
};

#define __list_for_each_node(__list, __node, __start_fn, __iter_fn) \
	for (struct node *__n = __start_fn(__list), *__next; \
	     __n && (__node = __n) && ((__next = __iter_fn(__n)) || (!__next)); \
	     __n = __next)

#define list_for_each_node(__list, __node) \
	__list_for_each_node(__list, __node, list_head, list_next)

#define list_for_each_node_reverse(__list, __node) \
	__list_for_each_node(__list, __node, list_tail, list_prev)

#define __list_for_each(__list, __item, __start_fn, __iter_fn) \
	for (struct node *__node = __start_fn(__list); \
	     __node && ((__item = __node->item) || (!__item)); \
	     __node = __iter_fn(__node))

#define list_for_each(__list, __item) \
	__list_for_each(__list, __item, list_head, list_next)

#define list_for_each_reverse(__list, __item) \
	__list_for_each(__list, __item, list_tail, list_prev)

/**
 * @brief Initializes a list object.
 *
 * Initializes a list object to reflect an empty state.
 *
 * @param[in]      list           pointer to a list object.
 * @param[in]      item_free_fn   custom callback for deallocating list items.
 *                                If NULL, free() will be used.
 */
static inline void list_init(struct list *list, item_free_t item_free_fn)
{
	list->head = NULL;
	list->tail = NULL;
	if (item_free_fn)
		list->item_free = item_free_fn;
	else
		list->item_free = free;
}

/**
 * @brief Clears a list and frees the items it contains.
 *
 * This function releases the memory allocated for a list object. It also frees
 * the data items attached to list nodes. It does not free the list itself.
 *
 * @param[in]      list           pointer to a list object.
 */
static inline void list_erase(struct list *list)
{
	void __list_erase(struct list *list, item_free_t free_fn);
	__list_erase(list, list->item_free);
}

/**
 * @brief Clears a list.
 *
 * This function removes and deallocates all nodes from the list. It does not
 * free the data items, to do that use list_erase().
 *
 * @param[in]      list           pointer to a list object.
 */
static inline void list_clear(struct list *list)
{
	void __list_erase(struct list *list, item_free_t free_fn);
	__list_erase(list, NULL);
}

/**
 * @brief Removes an element from the list.
 *
 * This function removes an element from the list. It only detaches the element
 * and does not release the memory allocated for the element. To free memory
 * allocated for an element use list_delete() instead.
 *
 * @param[in]      node           pointer to a node object.
 */
static inline void list_remove(struct node *node)
{
	void __list_remove(struct node *node, item_free_t free_fn);
	__list_remove(node, NULL);
}

/**
 * @brief Removes an element from the list and releases its memory.
 *
 * This function removes an element from the list and frees the memory allocated
 * for the list node and data item.
 *
 * @param[in]      node           pointer to a node object.
 */
static inline void list_delete(struct node *node)
{
	void __list_remove(struct node *node, item_free_t free_fn);
	__list_remove(node, node->list->item_free);
	free(node);
}

/**
 * @brief Inserts an element into the list.
 *
 * This function puts an element after a given element.
 *
 * @param[in]      list           pointer to list object.
 * @param[in]      item           data item to be inserted into the list.
 * @param[in]      after          list node after which to insert the element.
 *                                If NULL, then insert at the head of the list.
 */
void list_insert(struct list *list, void *item, struct node *after);

/**
 * @brief Appends an element to the end of the list.
 *
 * This function puts an element on tail of a list.
 *
 * @param[in]      list           pointer to list object.
 * @param[in]      item           data item to be inserted into the list.
 */
static inline void list_append(struct list *list, void *item)
{
	list_insert(list, item, list->tail);
}

/**
 * @brief Reruns next element.
 *
 * This function returns next element relatively to the given element.
 *
 * @param[in]      node           pointer to a node object.
 *
 * @return Pointer to an element if successful. The NULL pointer means
 *         that node is the last element on the list.
 */
static inline struct node *list_next(const struct node *node)
{
	return node->next;
}

/**
 * @brief Returns previous element.
 *
 * This function returns previous element relatively to the given element.
 *
 * @param[in]      node           pointer to a node object.
 *
 * @return Pointer to an element if successful. The NULL pointer means
 *         that node is the first element on the list.
 */
static inline struct node *list_prev(const struct node *node)
{
	return node->prev;
}

/**
 * @brief Returns head of a list.
 *
 * This function returns a head of a list.
 *
 * @param[in]      list           pointer to a list object.
 *
 * @return Pointer to an element if successful. The NULL pointer means that
 *         there's no element on a list.
 */
static inline struct node *list_head(const struct list *list)
{
	return list->head;
}

/**
 * @brief Returns tail of a list.
 *
 * This function returns a tail of a list.
 *
 * @param[in]      list           pointer to a list object.
 *
 * @return Pointer to an element if successful. The NULL pointer means that
 *         there's no element on a list.
 */
static inline struct node *list_tail(const struct list *list)
{
	return list->tail;
}

/**
 * @brief Checks if a list is empty.
 *
 * This function checks if a list object has elements.
 *
 * @param[in]      list           pointer to a list object.
 *
 * @return 1 if list is empty, otherwise the function returns 0.
 */
static inline int list_is_empty(const struct list *list)
{
	return (list->head == NULL);
}

#endif				/* _LIST_H_INCLUDED_ */
