#ifndef __LIST_H
#define __LIST_H

#include <sys/types.h>
#include <debug.h>

template <class T> class List;

template <class T>
class ListNode {
  public:
	ListNode(T *container) :
		mListPrev(0),
		mListNext(0),
		mList(0),
		mContainer(container) {}

	List<T> *Remove(); // remove from whatever list it may be present in, returns list it was removed from
	void AddAfter(ListNode <T> *node);
	void AddBefore(ListNode <T> *node);

	ListNode<T> *NextNode();
	ListNode<T> *PrevNode();
    T *Next();
    T *Prev();

	bool IsInList() const { return mList != NULL; }
	List<T> ContainerList() const { return mList; }

  private:
	ListNode<T> *mListPrev;
	ListNode<T> *mListNext;
	List<T> *mList;
	T *mContainer;

	friend class List<T>; // all List instances can mess with our internals
};

template <class T>
List<T> *ListNode<T>::Remove()
{
	ASSERT(mList != NULL && mListPrev != NULL && mListNext != NULL);

	mListNext->mListPrev = mListPrev;
	mListPrev->mListNext = mListNext;
	mListNext = mListPrev = 0;
	
	mList->count--;

	List<T> *list = mList;
	mList = 0;

	return list;
}

template <class T>
void ListNode<T>::AddAfter(ListNode <T> *node)
{
	ASSERT(mListPrev == NULL && mListPrev == NULL && mList == NULL);

	// add this node after the passed in node, and to whatever list the passed in node belongs to
	mListNext = node->mListNext;
	mListNext->mListPrev = this;
	mListPrev = node;
	mListPrev->mListNext = this;
	mList = node->mList;
	mList->count++;
}

template <class T>
void ListNode<T>::AddBefore(ListNode <T> *node)
{
	ASSERT(mListPrev == NULL && mListPrev == NULL && mList == NULL);

	// add this node after the passed in node, and to whatever list the passed in node belongs to
	mListPrev = node->mListPrev;
	mListPrev->mListNext = this;
	mListNext = node;
	mListNext->mListPrev = this;
	mList = node->mList;
	mList->count++;
}

template <class T>
inline ListNode<T> *ListNode<T>::NextNode()
{
	if (mListNext == &mList->mList)
		return NULL;

	return mListNext;
}

template <class T>
inline ListNode<T> *ListNode<T>::PrevNode()
{
	if (mListPrev == &mList->mList)
		return NULL;

	return mListPrev;
}

template <class T>
inline T *ListNode<T>::Next()
{
	ListNode<T> *node = NextNode();
	if (node == NULL)
		return NULL;

	return node->mContainer;
}

template <class T>
inline T *ListNode<T>::Prev()
{
	ListNode<T> *node = PrevNode();
	if (node == NULL)
		return NULL;

	return node->mContainer;
}

template <class T> 
class List {
  public:
    List();

	// accessors
	void AddToHead(ListNode<T> *node);
	void AddToTail(ListNode<T> *node);

	// remove head/tail
	ListNode<T> *PopHeadNode();
	ListNode<T> *PopTailNode();
	T *PopHead();
	T *PopTail();

	// look at head/tail
	ListNode<T> *HeadNode();
	ListNode<T> *TailNode();
	T *Head();
	T *Tail();

	// List's count
	int Count() const { return count; }

	// Is a node inside this list?
	bool NodeInList(ListNode<T> *node) const { return node->mList == this; }
	
	void Dump(void (*nodecallback)(T *node) = NULL);

  private:
	ListNode<T> mList;
	int count; // queue count

	friend class ListNode<T>;
};

template <class T>
List<T>::List() 
	: mList((T *)this),
	  count(0)
{
	mList.mListNext = mList.mListPrev = &mList;
	mList.mList = this;
}

template <class T>
inline void List<T>::AddToHead(ListNode<T> *node)
{
	node->AddAfter(&mList);
}

template <class T>
inline void List<T>::AddToTail(ListNode<T> *node)
{
	node->AddBefore(&mList);
}

template <class T>
inline ListNode<T> *List<T>::PopHeadNode()
{
	if (mList.mListNext == &mList) 
		return NULL; // empty

	ListNode<T> *node = mList.mListNext;
	node->Remove();
	return node;
}

template <class T>
inline ListNode<T> *List<T>::PopTailNode()
{
	if (mList.mListNext == &mList)
		return NULL; // empty

	ListNode<T> *node = mList.mListPrev;
	node->Remove();
	return node;
}

template <class T>
inline T *List<T>::PopHead()
{
	ListNode<T> *node = PopHeadNode();
	if (!node)
		return NULL;

	return node->mContainer;
}

template <class T>
inline T *List<T>::PopTail()
{
	ListNode<T> *node = PopTailNode();
	if (!node)
		return NULL;

	return node->mContainer;
}

template <class T>
inline ListNode<T> *List<T>::HeadNode()
{
	if (mList.mListNext == &mList) 
		return NULL; // empty
	else
		return mList.mListNext;
}

template <class T>
inline ListNode<T> *List<T>::TailNode()
{
	if (mList.mListNext == &mList)
		return NULL; // empty
	else
		return mList.mListPrev;
}

template <class T>
inline T *List<T>::Head()
{
	ListNode<T> *node = HeadNode();
	if (!node)
		return NULL;

	return node->mContainer;
}

template <class T>
inline T *List<T>::Tail()
{
	ListNode<T> *node = TailNode();
	if (!node)
		return NULL;

	return node->mContainer;
}


template <class T>
void List<T>::Dump(void (*nodecallback)(T *node))
{
	dprintf("%s: queue at %p\n", __PRETTY_FUNCTION__, this);
	dprintf("\thead %p tail %p count %d\n", mList.mListNext, mList.mListPrev, count);
	
	if (mList.mListNext == &mList) {
		dprintf("\tqueue is empty!\n");
	} else {
		ListNode<T> *node;
		for(node = mList.mListNext; node != &mList; node = node->mListNext) {
			dprintf("\tnode %p, container %p\n", node, node->mContainer);
			if (nodecallback)
				nodecallback(node->mContainer);
		}
	}
}

#endif
