#ifndef TOOLBOX_LIST_H
#define TOOLBOX_LIST_H

#include "tools.h"
#include "ccollection.h"
//#include "cexception.h"
//#include "cbasic.h"

#include <iostream>

using namespace std;
		
template<class T>
struct cTBListItem {
	cTBListItem<T> *Next;
	cTBListItem<T> *Prev;
	T      Data;
};

// ANSI/ISO C++ and GCC4 require template friend functions to be forward-declared...
template <class T> class cTBList;
template <class T> inline cTBList<T> &operator+= (cTBList<T> &list1, const cTBList<T> &list2);
template <class T> inline cTBList<T> &operator+= (cTBList<T> &list, const T &element);
template <class T> inline cTBList<T> operator+ (const cTBList<T> &list1, const cTBList<T> &list2);
template <class T> inline cTBList<T> operator+ (const T &element, const cTBList<T> &list);
template <class T> inline cTBList<T> operator+ (const cTBList<T> &list, const T &element);


template <class T>
class cTBList: public cCollection {
	protected:

		cTBListItem<T> *newItem (cTBListItem<T> *i1, cTBListItem<T> *i2) const;
		void delItem (cTBListItem<T> *i) const;

	private:
		cTBListItem<T> *First;
		cTBListItem<T> *Last;
		cTBListItem<T> *Current;
		int    CurPos;
		uint   Count;

		cTBListItem<T> *locate (uint idx) const;
		cTBListItem<T> *unlink ();
		cTBListItem<T> *getCurrent () const { return Current; }

		void init ();

	public:
		cTBList ();
		cTBList (const cTBList<T> &src);
		virtual ~cTBList ();

		cTBList<T> &operator= (const cTBList<T> &src);

		uint count () const { return Count; }

		void append   (const T &obj);
		void prepend  (const T &obj);
		bool insertAt (uint pos, const T &obj);

		T &append  ();
		T &prepend ();

		void append   (const cTBList<T> &src);
		void prepend  (const cTBList<T> &src);
		bool insertAt (uint pos, const cTBList<T> &src);

		bool remove   (const T &obj);
		bool remove   ();
		bool removeAt (uint idx);

		bool removeFirst () { setFirstCurrent(); return remove(); }
		bool removeLast  () { setLastCurrent();  return remove(); }

		T take      ();
		T takeAt    (uint idx);

		T takeFirst () { setFirstCurrent(); return take(); }
		T takeLast  () { setLastCurrent();  return take(); }

		void clear ();

		int find     (const T &obj);
		int contains (const T &obj) const;
		
		int current  () const { return CurPos; }

		T  at (uint idx) const;
		T &at (uint idx);

		T  operator[] (uint idx) const { return at(idx); }
		T &operator[] (uint idx) { return at(idx); }

		const T &get   () const;
		const T &first () const;
		const T &last  () const;
		const T &next  () const;
		const T &prev  () const;
		
		T &setFirstCurrent ();
		T &setLastCurrent  ();
		T &setNextCurrent  ();
		T &setPrevCurrent  ();

		friend cTBList<T> &operator+= <> (cTBList<T> &list1, const cTBList<T> &list2);
		friend cTBList<T> &operator+= <> (cTBList<T> &list, const T &element);

		friend cTBList<T> operator+ <> (const cTBList<T> &list1, const cTBList<T> &list2);
		friend cTBList<T> operator+ <> (const T &element, const cTBList<T> &list);
		friend cTBList<T> operator+ <> (const cTBList<T> &list, const T &element);

		/*friend cSource &operator<< <> (cSource &dest, const cTBList<T> &list);
		friend cSource &operator>> <> (cSource &dest, cTBList<T> &list);*/
};

// Template Implementation (inline funcs)

template<class T>
inline int cTBList<T>::find (const T & i) {
	cTBListItem<T> *item = First;
	int index = 0;

	while (item && (item->Data != i)) {
		item = item->Next;
		index++;
	}

	Current = item;
	return (CurPos = item ? index : -1);
}

template<class T>
inline int cTBList<T>::contains (const T &i) const {
	cTBListItem<T> *item = First;
	uint count = 0;

	while (item) {
		if (item->Data == i)
			count++;
		item = item->Next;
	}
	return count;
}

template<class T>
inline T &cTBList<T>::at (uint idx) {
	ASSERT(idx >= Count);
	return locate(idx)->Data;
}

template<class T>
inline T cTBList<T>::at (uint idx) const {
	ASSERT(idx >= Count);
	return locate(idx)->Data;
}

template<class T>
inline const T &cTBList<T>::get () const {
	ASSERT(Current == NULL);
	return Current->Data;
}

template<class T>
inline const T &cTBList<T>::first () const {
	ASSERT(First == NULL);
	return First->Data;
}

template<class T>
inline const T &cTBList<T>::last () const {
	ASSERT(Last == NULL);
	return Last->Data;
}

template<class T>
inline const T &cTBList<T>::next () const {
	ASSERT(Current == NULL);
	ASSERT(Current->Next == NULL);
	return Current->Next->Data;
}

template<class T>
inline const T &cTBList<T>::prev () const {
	ASSERT(Current == NULL);
	ASSERT(Current->Prev == NULL);
	return Current->Prev->Data;
}

// Template implementation (out-of-line)

template<class T>
cTBList<T>::cTBList () : cCollection () {
	init();
}

template<class T>
cTBList<T>::cTBList (const cTBList<T> &src) : cCollection () {
	init();
	append(src);
}

template<class T>
cTBList<T>::~cTBList () {
	if (Count)
		clear();
}

template<class T>
void cTBList<T>::init () {
	First = Last = Current = NULL;
	Count = 0;
	CurPos = -1;
}

template<class T>
cTBListItem<T> *cTBList<T>::newItem (cTBListItem<T> *pBefore, cTBListItem<T> *pAfter) const {
	cTBListItem<T> *item = new cTBListItem<T>;

	if (item == NULL)
		//throwMemoryException(sizeof(cTBListItem<T>));
		cerr << "Error: Out of Memory while trying to allocate " 
			 << sizeof(cTBListItem<T>) << " bytes" << endl;
		

	item->Next = pAfter;
	item->Prev = pBefore;

	if (pBefore)
		pBefore->Next = item;

	if (pAfter)
		pAfter->Prev  = item;

	createElements<T>(&item->Data, 1);

	return item;
}

template<class T>
void cTBList<T>::delItem (cTBListItem<T> *item) const {
	ASSERT(item == NULL);
	deleteElements<T>(&item->Data, 1);
	delete item;
}

template<class T>
cTBListItem<T> *cTBList<T>::locate (uint idx) const {
	if ((int)idx == CurPos)
		return Current;

	cTBListItem<T> *item;
	int dist = idx - CurPos;
	bool direct;

	if (idx >= Count)
		return 0;

	if (dist < 0)
		dist = -dist;

	if (((uint)dist < idx) && 
			((uint)dist < Count - idx)) {
		item = Current;
		direct = idx > (uint)CurPos;
	} else if (idx < Count - idx) {
		item = First;
		dist = idx;
		direct = true;
	} else {
		item = Last;
		dist = Count - idx - 1;
		if (dist < 0)
			dist = 0;
		direct = false;
	}

	while (dist--)
		item = direct ? item->Next : item->Prev;

	return item;
}

template<class T>
T &cTBList<T>::prepend () {
	cTBListItem<T> *item = newItem(NULL, First);

	First = Current = item;
	if (!Last)
		Last = First;

	Count++;
	CurPos = 0;

	return item->Data;
}

template<class T>
T &cTBList<T>::append () {
	cTBListItem<T> *item = newItem(Last, NULL);

	Last = Current = item;
	if (!First)
		First = Last;

	CurPos = Count;
	Count++;

	return item->Data;
}

template<class T>
void cTBList<T>::prepend (const T &i) {
	prepend() = i;
}

template<class T>
void cTBList<T>::append (const T & i) {
	append() = i;
}

template<class T>
bool cTBList<T>::insertAt (uint idx, const T & i) {
	if (!idx) {
		prepend(i);
		return true;
	} else if (idx == Count) {
		append(i);
		return true;
	}

	cTBListItem<T> *nextItem = locate(idx);
	if (!nextItem)
		return false;

	cTBListItem<T> *prevItem = nextItem->Prev;

	cTBListItem<T> *item = newItem(prevItem, nextItem);
	item->Data = i;

	CurPos = idx;
	Current = item;
	Count++;

	return true;
}

template<class T>
cTBListItem<T> *cTBList<T>::unlink () {
	if (!Current)
		return NULL;

	cTBListItem<T> *item = Current;
	if (item == First) {
		if ((First = item->Next))
			First->Prev = NULL;
		else {
			Last = Current = NULL;
			CurPos = -1;
		}
	} else if (item == Last) {
		Last = item->Prev;
		Last->Next = NULL;
	} else {
		item->Prev->Next = item->Next;
		item->Next->Prev = item->Prev;
	}

	if (item->Next)
		Current = item->Next;
	else if (item->Prev) {
		Current = item->Prev;
		CurPos--;
	}
	Count--;
	return item;
}

template<class T>
bool cTBList<T>::remove (const T & i) {
	if (i)
		if (find(i) == -1)
			return false;

	cTBListItem<T> *item = unlink();
	if (!item)
		return false;

	delItem(item);
	return true;
}

template<class T>
bool cTBList<T>::removeAt (uint idx) {
	if (!locate(idx))
		return false;

	cTBListItem<T> *item = unlink();
	if (!item)
		return false;

	delItem(item);
	return true;
}

template<class T>
T cTBList<T>::take () {
	ASSERT(Current == NULL);

	cTBListItem<T> *item = unlink();
	T data = item->Data;
	delItem(item);
	return data;
}

template<class T>
T cTBList<T>::takeAt (uint idx) {
	ASSERT(idx >= Count);

	locate(idx);

	return take();
}

template<class T>
void cTBList<T>::clear () {
	cTBListItem<T> *item = First;

	First = Last = Current = NULL;
	Count = 0;
	CurPos = -1;

	cTBListItem<T> *prev;
	while (item) {
		prev = item;
		item = item->Next;
		delItem(prev);
	}
}

template<class T>
T &cTBList<T>::setFirstCurrent () {
	ASSERT(First == NULL);
	CurPos = 0;
	return (Current = First)->Data;
}

template<class T>
T &cTBList<T>::setLastCurrent () {
	ASSERT(Last == NULL);
	CurPos = Count - 1;
	return (Current = Last)->Data;
}

template<class T>
T &cTBList<T>::setNextCurrent () {
	ASSERT(Current == NULL);
	ASSERT(Current->Next == NULL);
	CurPos++;
	return (Current = Current->Next)->Data;
}

template<class T>
T & cTBList<T>::setPrevCurrent () {
	ASSERT(Current == NULL);
	ASSERT(Current->Prev == NULL);
	CurPos--;
	return (Current = Current->Prev)->Data;
}

template<class T>
void cTBList<T>::append (const cTBList<T> &src) {
	ASSERT(src.First == NULL);
	for (cTBListItem<T> *next = src.First; next; next = next->Next)
		append(next->Data);
}

template<class T>
void cTBList<T>::prepend (const cTBList<T> &src) {
	ASSERT(src.First == NULL);
	for (cTBListItem<T> *next = src.First; next; next = next->Next)
		prepend(next->Data);
}

template<class T>
bool cTBList<T>::insertAt (uint idx, const cTBList<T> &src) {
	ASSERT(src.First == NULL);

	if (idx >= Count)
		return false;

	for (cTBListItem<T> *next = src.First; next; next = next->Next)
		insertAt(idx, next->Data);
}

// Operators (all inline)

template<class T>
inline cTBList<T> &cTBList<T>::operator= (const cTBList<T> &src) {
	clear();
	append(src);
	return *this;
}

template<class T>
inline cTBList<T> &operator+= (cTBList<T> &list1, const cTBList<T> &list2) {
	list1.append(list2);
	return list1;
}

template<class T>
inline cTBList<T> &operator+= (cTBList<T> &list, const T &element) {
	list.append(element);
	return list;
}

template<class T>
inline cTBList<T> operator+ (const cTBList<T> &list1, const cTBList<T> &list2) {
	cTBList<T> neu = list1;
	neu.append(list2);
	return neu;
}

template<class T>
inline cTBList<T> operator+ (const T &element, const cTBList<T> &list) {
	cTBList<T> neu = list;
	neu.prepend(element);
	return neu;
}

template<class T>
inline cTBList<T> operator+ (const cTBList<T> &list, const T &element) {
	cTBList<T> neu = list;
	neu.append(element);
	return neu;
}

/*template<class T>
cSource &operator<< (cSource &dest, const cTBList<T> &list) {
	cLong anzahl = list.count();
	dest << anzahl;

	for (int i = 0; i < anzahl; ++i)
		dest << list[i];
}*/

/*template<class T>
cSource &operator>> (cSource &dest, cTBList<T> &list) {
	cLong anzahl;
	dest >> anzahl;

	for (int i = 0; i < anzahl; ++i)
		dest >> list.append();

	return dest;
}*/

/*
typedef class cTBList<cLong> cLongList;
typedef class cTBList<cBoolean> cBooleanList;
typedef class cTBList<cDouble> cDoubleList;
typedef class cTBList<cChar> cCharList;

typedef class cTBList<cString> cStringList;

typedef class cTBList<char*> cPtrList;

// Special Overrides for Lists of template classes
template<class T>
inline cTBList<cBasic<T> > &operator+= (cTBList<cBasic<T> > &src, const T &item) {
	src.append((cBasic<T>)item);
	return src;
}

template<class T>
inline cTBList<cBasic<T> > operator+ (const cTBList<cBasic<T> > &src, const T &item) {
	cTBList<cBasic<T> > neu = src;
	neu.append((cBasic<T>)item);
	return neu;
}

template<class T>
inline cTBList<cBasic<T> > operator+ (const T &src, const cTBList<cBasic<T> > &list) {
	cTBList<cBasic<T> > neu = list;
	neu.prepend((cBasic<T>)src);
	return neu;
}

// Special Overrides for String lists
template<class T>
inline cTBList<T> &operator+= (cTBList<T> &src, const char *item); // Normally forbidden

template<>
inline cTBList<cString> &operator+= <cString> (cTBList<cString> &src, const char *item) {
	src.append((cString)item);
	return src;
}
*/

// Special Overrides for Lists of Lists
template<class T>
inline cTBList<cTBList<T> > &operator+= (cTBList<cTBList<T> > &src, const T &item) {
	src.Last->append(item);
	return src;
}

template<class T>
inline cTBList<cTBList<T> > operator+ (const cTBList<cTBList<T> > &src, const T &item) {
	cTBList<cTBList<T> > neu = src;
	neu.Last->append(item);
	return neu;
}

template<class T>
inline cTBList<cTBList<T> > operator+ (const T &item, const cTBList<cTBList<T> > &list) {
	cTBList<cTBList<T> > neu = list;
	list.Last->append(item);
	return neu;
}

#endif // TOOLBOX_LIST_H
