#ifndef LIST_H
#define LIST_H

#include "fw.h"

typedef uint32_t key_t;

/* --- Doubly linked list (head node assumed) --- */

struct dll {
    struct dll *prev, *next;
};

static inline void dll_init(struct dll *head)
{
    head->prev = head->next = head;
}

static inline struct dll *dll_find_offs(struct dll *head, key_t key,
					ptrdiff_t key_offset)
{
    for (const struct dll *nh = head->next; nh != head; nh = nh->next) {
	const key_t *node_key = (const key_t *)((const char *)nh + key_offset);
	if (*node_key == key)
	    return (struct dll *)nh;
    }

    return NULL;
}

#define dll_find(head,what,struc,list,key)				\
    ({									\
    struct dll *node = dll_find_offs(head, what, offset_diff(struc,key,list)); \
    (struc *)(node ? container_of(node, struc, list) : NULL);	\
    })

/* Remove from linked list */
static inline void dll_remove(struct dll *node)
{
    node->prev->next = node->next;
    node->next->prev = node->prev;
    node->next       = node;
    node->prev       = node;
}

/* Add to linked list */
static inline void dll_insert_head(struct dll *head, struct dll *node)
{
    node->prev       = head;
    node->next       = head->next;
    head->next       = node;
    node->next->prev = node;
}
static inline void dll_insert_tail(struct dll *head, struct dll *node)
{
    node->next       = head;
    node->prev       = head->prev;
    head->prev       = node;
    node->prev->next = node;
}

/* Move to beginning of linked list */
static inline void dll_promote(struct dll *head, struct dll *node)
{
    dll_remove(node);
    dll_insert_head(head, node);
}

/* Move to end of linked list */
static inline void dll_demote(struct dll *head, struct dll *node)
{
    dll_remove(node);
    dll_insert_tail(head, node);
}

#endif /* LIST_H */
