/* Copyright (C) 2021,2022 fef <owo@fef.moe>.  All rights reserved. */

#include <gay/clist.h>
#include <gay/config.h>
#include <gay/kprintf.h>
#include <gay/poison.h>
#include <gay/systm.h>

#if CFG_DEBUG_CLIST
#define CLIST_DEBUG_BLOCK
#define CLIST_ASSERT(x) KASSERT(x)
#else
#define CLIST_DEBUG_BLOCK if (0)
#define CLIST_ASSERT(x) ({})
#endif

void clist_init(struct clist *head)
{
	CLIST_DEBUG_BLOCK {
		if (head->next == head && head->prev == head)
			kprintf("clist_init(%p) called multiple times\n", head);
	}

	head->next = head;
	head->prev = head;
}

void clist_add(struct clist *head, struct clist *new)
{
	CLIST_ASSERT(!sus_nil(head->next));
	CLIST_ASSERT(!sus_nil(new));

	head->prev->next = new;
	new->next = head;

	new->prev = head->prev;
	head->prev = new;
}

void clist_add_first(struct clist *head, struct clist *new)
{
	head->next->prev = new;
	new->next = head->next;

	new->prev = head;
	head->next = new;
}

void clist_del(struct clist *node)
{
	node->next->prev = node->prev;
	node->prev->next = node->next;

	CLIST_DEBUG_BLOCK {
		node->next = (struct clist *)CLIST_POISON_NEXT;
		node->prev = (struct clist *)CLIST_POISON_PREV;
	}
}

struct clist *clist_del_first(struct clist *head)
{
	CLIST_ASSERT(!clist_is_empty(head));

	struct clist *first = head->next;
	clist_del(first);
	return first;
}

struct clist *clist_del_last(struct clist *head)
{
	CLIST_ASSERT(!clist_is_empty(head));

	struct clist *last = head->prev;
	clist_del(last);
	return last;
}