/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


#ifndef	__NLIST__
#define	__NLIST__


		#include "alloc/ialloc.h"
		#include "assert/nassert.h"


namespace GS {

/*
	@short	List.
	@author	Emmanuel Julien (ejulien@gsworks.fr)
*/
template <class T> class List
{
public:

		//----------------------------------------------------------------------
		class Item
		{
				friend class List <T>;

				T				o;
				Item			*p, *n;

		public:

				NPLACEMENT_NEW(ListItem)

				/// Return the previous list link.
		inline	Item			*Previous() const	{	return p;	}
				/// Return the next list link.
		inline	Item			*Next() const		{	return n;	}

				/// Retrieve a reference to the item object.
		inline	T				&Object()			{	return o;	}

				Item(const T &obj)
				{
					o = obj;
					p = n = 0;
				}
		};
		//----------------------------------------------------------------------

		//----------------------------------------------------------------------
		class Iterator
		{
				Item		*c, *n;

		public:

		inline	void		operator++()
				{
					if	((c = c ? n : 0) != 0)
						n = c->n;
				}

		inline	bool		IsOver() const		{	return c ? false : true;	}
		inline	Item		*Next() const		{	return n;	}

		inline	Item		*GetItem() const	{	return c;	}
		inline	T			&Object()			{	return c->Object();	}

				/// Only use when using a pointer type.
		inline	T			ObjectPtr()			{	return c ? c->Object() : 0;	}

				void		Reset(Item *start = 0)
				{
					c = start;
					n = c ? c->Next() : 0;
				}
				Iterator(Item *start = 0)
				{	Reset(start);	}
		};
		//----------------------------------------------------------------------

protected:

		uint			count;
		Item			*root, *last;

public:

		/// Get item count in list.
inline	uint			GetCount() const { return count; }
		/// Get root item.
inline	Item			*GetRoot() const { return root; }
		/// Get last item.
inline	Item			*GetLast() const { return last; }

		/// Clone list.
		void			Clone(List <T> &clone_list) const
		{
			clone_list.Clear();
			for	(Item *_i = root; _i; _i = _i->Next())
				clone_list.Add(_i->Object());
		}

		/// Get item from position.
		Item			*ItemAt(uint n) const
		{
			if	(n > count)
				return 0;

			Item *p = root;
			while (n--)
				p = p->n;

			return p;
		}
inline	T				&ObjectAt(uint n) const
		{	return ItemAt(n)->Object();	}
inline	T				&operator[] (uint n) const
		{	return ItemAt(n)->Object();	}

		/// Get index of a given item.
		int				Index(const T &o) const
		{
			int pos = 0;
			for	(Item *p = root; p; p = p->n)
			{
				if	(p->Object() == o)
					return pos;
				pos++;
			}
			return -1;
		}

		/// Check whether a given item belongs to this list or not.
		bool			Belongs(Item *i) const
		{
			for	(Item *s = root; s; s = s->n)
				if	(s == i)
					return true;
			return false;
		}

		/// Insert item after a given reference item (defaults to last).
virtual	Item			*Append(const T &o, Item *rfr = 0)
		{
			Item *i = new Item(o);
			if	(!i)
				return 0;

			if	(!rfr)
			{
				i->p = last;
				if	(last)
					last->n = i;
				else
					root = i;
				last = i;
			}
			else
			{
				if	(rfr->n)
					rfr->n->p = i;
				i->n = rfr->n;
				rfr->n = i;
				i->p = rfr;

				if	(last == rfr)	// Update last.
					last = i;
			}

			count++;
			return i;
		}

		/// Insert item before a given reference item (defaults to root).
virtual	Item			*Prepend(const T &o, Item *rfr = 0)
		{
			Item *i = new Item(o);
			if	(!i)
				return 0;

			if	(!rfr)
			{
				i->n = root;
				if	(root)
					root->p = i;
				else
					last = i;
				root = i;
			}
			else
			{
				if	(rfr->p)
					rfr->p->n = i;
				i->p = rfr->p;
				rfr->p = i;
				i->n = rfr;

				if	(root == rfr)	// Update root.
					root = i;
			}

			count++;
			return i;
		}

		/// Insert a value at a given position.
		Item			*Insert(const T &v, uint at)
		{
			Item *rfr = ItemAt(at);
			return rfr ? Prepend(v, rfr) : 0;
		}

		/// Add item to list.
		Item			*Add(const T &o, bool append, bool allow_duplicate)
		{
			if	(!allow_duplicate)
				if	(Item *i = Find(o))
					return i;
			return append ? Append(o) : Prepend(o);
		}
		/// Add item to list.
		Item			*Add(const T &o)
		{	return Append(o);	}

		/// Add item to list.
		List <T>		&operator << (const T &o)
		{
			Add(o);
			return *this;
		}

		/// Find item by reference to object.
		Item			*Find(const T &o) const
		{
			for	(Item *p = root; p; p = p->n)
				if	(p->Object() == o)
					return p;
			return 0;
		}

		/// Find item by object value.
		Item			*FindByValue(const T &o) const
		{
			for	(Item *p = root; p; p = p->n)
				if	(*p->Object() == *o)
					return p;
			return 0;
		}

		/*!
			@short		Subtract two given lists by object value.
			@warning	The resulting list holds pointers to the original objects.
		*/
static	List <T>		*SubtractByValue(const List <T> &what, const List <T> &from)
		{
			List <T> *list = new List <T>;
			if	(list)
				for	(Item *f_p = from.root; f_p; f_p = f_p->n)
					if	(!what.FindByValue(f_p->Object()))
						list->Add(f_p->Object());
			return list;
		}

		/*!
			@short		Subtract two given lists by object address.
			@warning	The resulting list holds pointers to the original objects.
		*/
static	List <T>		*SubtractByAddress(const List <T> &what, const List <T> &from)
		{
			List <T> *list = new List <T>;
			if	(list)
				for	(Item *f_p = from.root; f_p; f_p = f_p->n)
					if	(!what.Find(f_p->Object()))
						list->Add(f_p->Object());
			return list;
		}

		/// Filter out linked-list item function.
		template <typename F> void FilterOut(F filter)
		{
			for	(Item *c = GetRoot(); c; )
			{
				Item *n = c->Next();
				if	(filter(c->Object()))
					Remove(c);
				c = n;
			}
		}

		/// Build a new list from select items.
		template <typename F, typename P> uint Select(List <T> &out, F filter, const P &filter_param) const
		{
			out.Clear();
			for	(Item *c = GetRoot(); c; )
				if	(filter(c->Object(), filter_param))
					out.Add(c->Object());
			return out.GetCount();
		}

		/// Sort linked-list function.
		template <typename C> void Sort(C compare)
		{
			for	(bool swapped = true; swapped; )
			{
				swapped = false;

				for	(Item *c = GetRoot(); c; )
				{
					Item *n = c->Next();
					if	(!n)
						break;

					if	(compare(c->Object(), n->Object()) < 0)
					{
						swapped = true;

						c->n = n->n;
						n->n = c;
						n->p = c->p;
						c->p = n;

						if	(n->p)
								n->p->n = n;
						else	root = n;

						if	(c->n)
								c->n->p = c;
						else	last = c;
					}
					else
						c = n;
				}
			}
		}

		/// Merge sort linked-list function.
		template <typename C> void MergeSort(C compare)
		{
			Item *list = root, *tail = 0;
			if	(!list)
				return;

			for	(int insize = 1; ; insize *= 2)
			{
				Item *p = list;
				list = 0;
				tail = 0;

				int merge_count = 0;			// Count number of merges we do in this pass.

				while (p)
				{
					merge_count++;

					// Step along from p.
					Item *q = p;
					int psize = 0;
					for	( ; q && (psize < insize); ++psize)
						q = q->Next();

					// If q hasn't fallen off end, we have two lists to merge.
					int qsize = insize;

					// Now we have two lists, merge them.
					while (psize > 0 || (qsize > 0 && q))
					{
						Item *e;

						if	(!psize)											// p is empty, e must come from q.
						{	e = q; q = q->Next(); qsize--;	}
						else	if	(!qsize || !q)								// q is empty, e must come from p.
						{	e = p; p = p->Next(); psize--;	}
						else	if	(compare(p->Object(), q->Object()) <= 0)	// First element of p is lower (or same), e must come from p.
						{	e = p; p = p->Next(); psize--;	}
						else													// First element of q is lower; e must come from q.
						{	e = q; q = q->Next(); qsize--;	}

						// Add the next element to the merged list.
						if	(tail)
								tail->n = e;
						else	list = e;

						e->p = tail;
						tail = e;
					}

					// Now p has stepped `insize' places along, and q has too.
					p = q;
				}

				tail->n = 0;

				// If we have done only one merge, we're done.
				if	(merge_count <= 1)
					break;
			}

			root = list;
			last = tail;
		}

		/// Extract object from list.
		Item			*ExtractItem(const T &o)
		{
			Item*i = Find(o);
			return i ? ExtractItem(i) : 0;
		}

		/// Extract item from list.
		Item			*ExtractItem(Item *i)
		{
			if	(!i)
				return 0;

			__ASSERT__(Belongs(i));

			if	(i->p)
				i->p->n = i->n;
			else
			{
				if	(i->n)
					i->n->p = 0;
				root = i->n;
			}

			if	(i->n)
				i->n->p = i->p;
			else
			{
				if	(i->p)
					i->p->n = 0;
				last = i->p;
			}

			i->p = i->n = 0;

			--count;
			return i;
		}
		/// Extract item at position.
		Item			*ExtractAt(uint n)
		{
			Item *i = ItemAt(n);
			return i ? ExtractItem(i) : 0;
		}

		/// Remove item from list.
virtual	bool			Remove(Item *i)
		{
			if	(ExtractItem(i) == 0)
				return false;

			delete i;
			return true;
		}

		/// Remove item from list.
virtual	bool			Remove(const T &o)
		{
			return Remove(Find(o));
		}

		/// Remove item at position.
		bool			RemoveAt(uint n)
		{
			Item *i = ItemAt(n);
			return i ? Remove(i) : false;
		}

		/// Remove all items from the list.
virtual	void			Clear()
		{
			for (Item *p = root, *n; p; p = n)
			{
				n = p->n;
				delete p;
			}

			count = 0;
			root = last = 0;
		}

		List()
		{
			count = 0;
			root = last = 0;
		}
virtual	~List()
		{	Clear();	}
};

/*
	@short	Auto linked-list.
	@author	Emmanuel Julien (ejulien@gsworks.fr)
*/
template <class T> struct AutoList : public List <T>
{
virtual	bool			Remove(class List <T> ::Item *i)
		{
			T o = i->Object();
			if	(!List <T> ::Remove(i))
				return false;
			delete o;
			return true;
		}
virtual	bool			Remove(const T &o)
		{
			class List <T> ::Item *i = this->Find(o);
			return i ? this->Remove(i) : false;
		}

virtual	void			Clear()
		{
			for	(class List <T> ::Item *p = this->root; p; p = p->Next())
				delete p->Object();
			List <T> ::Clear();
		}
virtual	~AutoList()
		{	this->Clear();	}
};

/*
	@short	Shared object linked-list.
	@author	Emmanuel Julien (ejulien@gsworks.fr)
*/
template <class T> struct SharedList : public List <T>
{
virtual	class List <T> ::Item *Append(const T &o, class List <T> ::Item *rfr = 0)
		{
			class List <T> ::Item *i = List <T> ::Append(o, rfr);
			if	(i)
				o->AddRef();
			return i;
		}
virtual	class List <T> ::Item *Prepend(const T &o, class List <T> ::Item *rfr = 0)
		{
			class List <T> ::Item *i = List <T> ::Prepend(o, rfr);
			if	(i)
				o->AddRef();
			return i;
		}

virtual	bool			Remove(class List <T> ::Item *i)
		{
			T	o = i->Object();
			if	(!List <T> ::Remove(i))
				return false;
			o->RemoveRef();
			return true;
		}
virtual	bool			Remove(const T &o)
		{
			class List <T> ::Item *i = this->Find(o);
			return i ? this->Remove(i) : false;
		}

virtual	void			Clear()
		{
			for	(class List <T> ::Item *p = this->root; p; p = p->Next())
				p->Object()->RemoveRef();
			List <T> ::Clear();
		}
virtual	~SharedList()
		{	Clear();	}
};


//------------------------------------------------------------------------------
		// Delete all list entries.
#define	ListDeleteAllPtr(T, L) { class GS::List <T> ::Item *t; while ((t = (L).GetRoot()) != 0) { T _o = t->Object(); (L).Remove(t); delete(_o); } }
		// Iterate over a list of pointers.
#define	ListForeachPtr(T, V, L) \
		for	(class GS::List <T> ::Iterator iterator((L).GetRoot()); T V = iterator.ObjectPtr(); ++iterator)
		// Iterate over a list of objects.
#define	ListForeach(T, V, L) \
		for	(class GS::List <T> ::Iterator V((L).GetRoot()); V.IsOver() == false; ++V)

		/// Find item by using a template identification class.
template <typename T, typename F, typename P> T ListFindEx(const List <T> &list, F filter, const P &what)
{
	for	(class List <T> ::Item *p = list.GetRoot(); p; p = p->Next())
		if	(filter(p->Object(), what))
			return p->Object();
	return 0;
}
		/// Remove all items with a reference count of 1 from a shared list.
template <class T> uint	PurgeSharedList(SharedList <T *> &list)
{
	uint c = list.GetCount();
	ListForeachPtr(T *, t, list)
		if	(t->GetRefCount() == 1)
			list.Remove(iterator.GetItem());
	return c - list.GetCount();
}
//------------------------------------------------------------------------------

} // GS


#endif	// __NLIST__
