/*
	SuperCollider real time audio synthesis system
    Copyright (c) 2002 James McCartney. All rights reserved.
	http://www.audiosynth.com

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program 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 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 Street, Fifth Floor, Boston, MA 02110-1301  USA
*/

/*

A doubly linked list template.

*/

#pragma once

#include <stdexcept>
#include <assert.h>


// A Link can be a node in a list or a list itself.

template <class T>
class Link
{
public:
	Link() : mNext(this), mPrev(this) {}

	T* Prev() { return static_cast<T*>(mPrev); }
	T* Next() { return static_cast<T*>(mNext); }

	void RemoveLeaveDangling()
	{
		mPrev->mNext = mNext;
		mNext->mPrev = mPrev;
	}

	void Remove()
	{
		RemoveLeaveDangling();
		mNext = mPrev = this;
	}

	void InsertAfter(T *inLink)
	{
		mPrev = inLink;
		mNext = inLink->mNext;
		mNext->mPrev = this;
		mPrev->mNext = this;
	}

	void InsertBefore(T *inLink)
	{
		mNext = inLink;
		mPrev = inLink->mPrev;
		mNext->mPrev = this;
		mPrev->mNext = this;
	}

	T* Head() { return static_cast<T*>(mNext); }
	T* Tail() { return static_cast<T*>(mPrev); }

	T* PopHead();
	T* PopTail();
	void PushHead(T* inBuf);
	void PushTail(T* inBuf);

	bool ContainsBuf(T* inBuf);
	bool IsEmpty() { return mNext == this; }
	void BeEmpty() { mNext = mPrev = this; }

	void Cat(T* inLink);

	bool SanityCheck();
	void DebugDump();

//private:
// Codewarrior refuses to inline Next() in some places..
	Link<T> *mNext, *mPrev;
};

template <class T, class Alloc>
void MakeListEmpty(Link<T> *inLink, Alloc* inAlloc)
{
	Link<T>* link = inLink->mNext;
	while (link != inLink) {
		Link<T>* nextlink = link->mNext;
		// SC uses placement new extensively, so here we do a 'placement delete'.
		// Using DestructSelf allows me to have either virtual
		// or non virtual destructors in subclasses at the discretion of the subclass.
		((T*)(link))->DestructSelf();
		inAlloc->Free(static_cast<T*>(link));
		link = nextlink;
	}
	inLink->mNext = inLink->mPrev = inLink;
}

template <class T>
void Link<T>::PushHead(T* inLink)
{
	assert(SanityCheck());

	Link<T>* link = static_cast<Link<T>*>(inLink);
	link->InsertAfter(static_cast<T*>(this));

	assert(SanityCheck());
}

template <class T>
T* Link<T>::PopHead()
{
	assert(SanityCheck());
	if (IsEmpty()) return 0;

	Link<T>* link = mNext;

	link->Remove();

	assert(SanityCheck());
	return static_cast<T*>(link);
}

template <class T>
void Link<T>::PushTail(T* inLink)
{
	assert(SanityCheck());

	Link<T>* link = static_cast<Link<T>*>(inLink);
	link->InsertBefore(static_cast<T*>(this));

	assert(SanityCheck());
}

template <class T>
T* Link<T>::PopTail()
{
	assert(SanityCheck());
	if (IsEmpty()) return 0;

	Link<T>* link = mPrev;
	link->Remove();

	assert(SanityCheck());
	return static_cast<T*>(link);
}

template <class T>
void Link<T>::Cat(T* inLink)
{
	assert(SanityCheck());

	Link<T>* link = static_cast<Link<T>*>(inLink);

	if (link->IsEmpty()) return;
	if (IsEmpty()) {
		mNext = link->mNext;
		mPrev = link->mPrev;
		link->mNext->mPrev = this;
		link->mPrev->mNext = this;
	} else {
		link->mNext->mPrev = mPrev;
		link->mPrev->mNext = this;
		mPrev->mNext = link->mNext;
		mPrev = link->mPrev;
	}
	link->mPrev = link;
	link->mNext = link;

	assert(SanityCheck());
}

template <class T>
bool Link<T>::ContainsBuf(T* inLink)
{
	Link<T>* link = static_cast<Link<T>*>(inLink);
	Link<T>* curLink = mNext;
	while (curLink != this) {
		if (curLink == link) return true;
		curLink = curLink->mNext;
	}
	return false;
}

template <class T>
void Link<T>::DebugDump()
{
	Link<T>* link = mNext;
	while (link != this) {
		//post("Link-> %08X   next %08X   prev %08X\n",
		//	link, link->mNext, link->mPrev);
		link = link->mNext;
	}
}

template <class T>
bool Link<T>::SanityCheck()
{
	Link<T>* link = mNext;
	while (link != this) {
		if (link->mPrev->mNext != link) {
			throw std::runtime_error("Link: bad link <-,->");
		}
		if (link->mNext->mPrev != link) {
			throw std::runtime_error("Link: bad link ->,<-");
		}
		link = link->mNext;
	}
	return true;
}
