/****************************************************************************
*                                                                           *
*  OpenNI 1.x Alpha                                                         *
*  Copyright (C) 2011 PrimeSense Ltd.                                       *
*                                                                           *
*  This file is part of OpenNI.                                             *
*                                                                           *
*  OpenNI is free software: you can redistribute it and/or modify           *
*  it under the terms of the GNU Lesser General Public License as published *
*  by the Free Software Foundation, either version 3 of the License, or     *
*  (at your option) any later version.                                      *
*                                                                           *
*  OpenNI 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 Lesser General Public License for more details.                      *
*                                                                           *
*  You should have received a copy of the GNU Lesser General Public License *
*  along with OpenNI. If not, see <http://www.gnu.org/licenses/>.           *
*                                                                           *
****************************************************************************/
#ifndef _XN_LIST_H
#define _XN_LIST_H

//---------------------------------------------------------------------------
// Includes
//---------------------------------------------------------------------------
#include <XnDataTypes.h>
#include <IXnNodeAllocator.h>
#include <XnNodeAllocator.h>
#include <XnNode.h>
#include <XnStatusCodes.h>

//---------------------------------------------------------------------------
// Types
//---------------------------------------------------------------------------

/**
 * The linked list
 */
class XnList
{
public:
	class ConstIterator
	{
	public:
		friend class XnList;

		/**
		* Copy constructor
		* 
		* @param	other	[in]	instance to copy from
		*/
		ConstIterator(const ConstIterator& other) : m_pCurrent(other.m_pCurrent) {}

		/**
		* Support ++iterator, go to the next object in the list
		*/
		ConstIterator& operator++()
		{
			m_pCurrent = m_pCurrent->Next();
			return *this;
		}

		/**
		* Support iterator++, go to the next object in the list, returning the old value
		*/
		ConstIterator operator++(int)
		{
			ConstIterator other(m_pCurrent);
			m_pCurrent = m_pCurrent->Next();
			return other;
		}

		/**
		* Support --iterator, go to the next object in the list
		*/
		ConstIterator& operator--()
		{
			m_pCurrent = m_pCurrent->Previous();
			return *this;
		}

		/**
		* Support iterator--, go to the next object in the list, returning the old value
		*/
		ConstIterator operator--(int)
		{
			ConstIterator other = *this;
			--*this;
			return other;
		}

		/**
		* Operator to check if 2 iterators point to the same object
		* 
		* @param	other	[in]	instance to compare with
		*/
		XnBool operator==(const ConstIterator& other) const
		{
			return m_pCurrent == other.m_pCurrent;
		}
		/**
		* Operator to check if 2 iterators point to different objects
		* 
		* @param	other	[in]	instance to compare with
		*/
		XnBool operator!=(const ConstIterator& other) const
		{
			return m_pCurrent != other.m_pCurrent;
		}

		/**
		* Get the value of the current object (const version)
		*/
		const XnValue& operator*() const
		{
			return m_pCurrent->Data();
		}


		/**
		* Get the entire current object (const version)
		*/
		const XnNode* GetNode() const
		{
			return m_pCurrent;
		}

		/**
		* Get the entire current object (non-const version)
		*/
		XnNode* GetNode()
		{
			return m_pCurrent;
		}

	protected:
		/**
		* constructor to be used from inside the XnList. It points to the node supplied.
		* 
		* @param	pNode	[in]	The XnNode to which to currently point
		*/
		ConstIterator(XnNode* pNode) : m_pCurrent(pNode) {}

		/** The current XnNode */
		XnNode* m_pCurrent;
	};

	/**
 	 * Iterator to the XnList
	 */
	class Iterator : public ConstIterator
	{
	public:
		friend class XnList;

		/**
		* Copy constructor
		* 
		* @param	other	[in]	instance to copy from
		*/
		inline Iterator(const Iterator& other) : ConstIterator(other) {}

		/**
		* Support ++iterator, go to the next object in the list
		*/
		inline Iterator& operator++() 
		{ 
			++(*(ConstIterator*)this);
			return (*this);
		}
		/**
		* Support iterator++, go to the next object in the list, returning the old value
		*/
		inline Iterator operator++(int) 
		{ 
			Iterator result = *this;
			++*this;
			return (result);
		}
		
		/**
		* Support --iterator, go to the next object in the list
		*/
		inline Iterator& operator--() 
		{ 
			--(*(ConstIterator*)this); 
			return (*this);
		}
		/**
		* Support iterator--, go to the next object in the list, returning the old value
		*/
		inline Iterator operator--(int)
		{ 
			Iterator result = *this;
			--*this;
			return (result);
		}

		/**
		* Get the value of the current object
		*/
		inline XnValue& operator*() const { return ((XnValue&)**(ConstIterator*)this); }

	protected:
		/**
		* constructor to be used from inside the XnList. It points to the node supplied.
		* 
		* @param	pNode	[in]	The XnNode to which to currently point
		*/
		inline Iterator(XnNode* pNode) : ConstIterator(pNode) {}
	};

public:
	/**
	* Constructor. Initialize internal representations
	*/
	XnList()
	{
		//Default node allocator is XnNodeAllocator
		Init(XN_NEW(XnNodeAllocator));
		m_bOwnsAllocator = TRUE;
	}

	/**
	* Destructor. Destroy internal representations
	*/
	virtual ~XnList()
	{
		Clear();

		// Return base node to the pool
		m_pNodeAllocator->Deallocate(m_pBase);

		if (m_bOwnsAllocator)
		{
			//We created the allocator in this object, so we must release it
			XN_DELETE(m_pNodeAllocator);
		}
	}

	/**
	* Add a new value at the beginning of list
	* 
	* @param	value	[in]	The value to add to the head of the list
	*
	* @return	XN_STATUS_ALLOC_FAILED	Failed to add to the list because no nodes are available.
	*/
	XnStatus AddFirst(const XnValue& value)
	{
		return Add(m_pBase, value);
	}

	/**
	* Add a new value at the end of the list
	* 
	* @param	value	[in]	The value to add to the tail of the list
	*
	* @return	XN_STATUS_ALLOC_FAILED	Failed to add to the list because no nodes are available.
	*/
	XnStatus AddLast(const XnValue& value)
	{
		return Add(rbegin().m_pCurrent, value);
	}

	/**
	* Add a new value after the object pointed to by the iterator
	* 
	* @param	where	[in]	iterator to the position after which to add the new value
	* @param	val		[in]	The value to add to the list
	*
	* @return	XN_STATUS_ALLOC_FAILED		Failed to add to the list because no nodes are available,
	*			XN_STATUS_ILLEGAL_POSITION	iterator is invalid
	*/
	XnStatus AddAfter(ConstIterator where, const XnValue& val)
	{
		if (where == end())
		{
			return XN_STATUS_ILLEGAL_POSITION;
		}

		return Add(where.m_pCurrent, val);
	}

	/**
	* Add a new value before the object pointed to by the iterator
	* 
	* @param	where	[in]	iterator to the position before which to add the new value
	* @param	val		[in]	The value to add to the list
	*
	* @return	XN_STATUS_ALLOC_FAILED		Failed to add to the list because no nodes are available,
	*/
	XnStatus AddBefore(ConstIterator where, const XnValue& val)
	{
		if (where == end())
		{
			return XN_STATUS_ILLEGAL_POSITION;
		}

		return Add(where.m_pCurrent->Previous(), val);
	}


	/**
	* Get an iterator pointing to a value in the list.
	* 
	* @param	value	[in]	The searched value 
	*
	* @return	end()	if value doesn't exist
	*/
	Iterator Find(const XnValue& value)
	{
		if (IsEmpty())
		{
			return end();
		}

		Iterator iter = begin();
		for (; iter != end(); ++iter)
		{
			if (*iter == value)
				break;
		}
		return iter;
	}


	/**
	* Get an iterator pointing to a value in the list.
	* 
	* @param	value	[in]	The searched value 
	*
	* @return	end()	if value doesn't exist
	*/
	ConstIterator Find(const XnValue& value) const
	{
		if (IsEmpty())
		{
			return end();
		}

		ConstIterator iter = begin();
		for (; iter != end(); ++iter)
		{
			if (*iter == value)
				break;
		}
		return iter;
	}


	/**
	* Remove a value from the list
	* 
	* @param	where	[in]	Iterator pointing to an entry in the list
	* @param	value	[out]	The value that was in the removed entry
	*
	* @return XN_STATUS_ILLEGAL_POSITION	iterator was invalid
	*/
	XnStatus Remove(ConstIterator where, XnValue& value)
	{
		value = *where;
		return Remove(where);
	}

	/**
	* Remove a value from the list
	* 
	* @param	where	[in]	Iterator pointing to an entry in the list
	*
	* @return XN_STATUS_ILLEGAL_POSITION	iterator was invalid
	*/
	virtual XnStatus Remove(ConstIterator where)
	{
		// Verify iterator is valid
		if (where == end())
		{
			return XN_STATUS_ILLEGAL_POSITION;
		}
		if (IsEmpty())
		{
			return XN_STATUS_IS_EMPTY;
		}

		XnNode* pToRemove = where.m_pCurrent;

		// Connect other nodes to bypass the one removed
		pToRemove->Previous()->Next() = pToRemove->Next();
		pToRemove->Next()->Previous() = pToRemove->Previous();

		// Return removed node to the pool
		m_pNodeAllocator->Deallocate(pToRemove);

		return XN_STATUS_OK;
	}


	/**
	* Remove all entries from the list
	*/
	XnStatus Clear()
	{
		while (!IsEmpty())
			Remove(begin());

		return XN_STATUS_OK;
	}

	/**
	* Check if list is empty
	*/
	XnBool IsEmpty() const
	{
		return (begin() == end());
	}

	/**
	* Current size of the list
	*/
	XnUInt32 Size() const
	{
		XnUInt32 nSize = 0;
		for (ConstIterator iter = begin(); iter != end(); ++iter, ++nSize)
			;

		return nSize;
	}

	/**
	* An iterator to the first entry of the list (non-const version)
	*/
	Iterator begin()
	{
		return Iterator(m_pBase->Next());
	}

	/**
	* An iterator to the first entry of the list (const version)
	*/
	ConstIterator begin() const
	{
		return ConstIterator(m_pBase->Next());
	}

	/**
	* An iterator 1to the end of the list (non-const version). The position is invalid.
	*/
	Iterator end()
	{
		return Iterator(m_pBase);
	}

	/**
	* An iterator to the end of the list (const version). The position is invalid.
	*/
	ConstIterator end() const
	{
		return ConstIterator(m_pBase);
	}

	/**
	* An iterator to the last entry of the list (non-const version)
	*/
	Iterator rbegin()
	{
		return Iterator(m_pBase->Previous());
	}

	/**
	* An iterator to the last entry of the list (const version)
	*/
	ConstIterator rbegin() const
	{
		return ConstIterator(m_pBase->Previous());
	}

	/**
	* An iterator to the beginning of the list (non-const version). This position is invalid
	*/
	Iterator rend()
	{
		return Iterator(m_pBase);
	}

	/**
	* An iterator to the beginning of the list (const version). This position is invalid
	*/
	ConstIterator rend() const
	{
		return ConstIterator(m_pBase);
	}
	
protected:
	friend class XnNodeManager;
	
	/**
	* Constructor. Initialize internal representations
	*/
	XnList(INiNodeAllocator* pNodeAllocator)
	{
		Init(pNodeAllocator);
		m_bOwnsAllocator = FALSE;
	}
	
	void Init(INiNodeAllocator* pNodeAllocator)
	{
		m_pNodeAllocator = pNodeAllocator;
		// Allocate a node to act as base node.
		m_pBase = m_pNodeAllocator->Allocate();
		if (m_pBase == NULL)
		{
			// OZOZ: Allocation failed in ctor...
		}

		m_pBase->Next() = m_pBase->Previous() = m_pBase;
	}

	/**
	* Add a new value to the list
	* 
	* @param	pWhere	[in]	The XnNode after which to add the new value
	* @param	val		[in]	The value to add to the list
	*
	* @return	XN_STATUS_ALLOC_FAILED		Failed to add to the list because no nodes are available,
	*/
	XnStatus Add(XnNode* pWhere, const XnValue& val)
	{
		// Get a node from the pool for the entry
		XnNode* pNewNode = m_pNodeAllocator->Allocate();
		if (pNewNode == NULL)
		{
			return XN_STATUS_ALLOC_FAILED;
		}
		// push new node to position
		pNewNode->Data() = val;
		pNewNode->Next() = pWhere->Next();
		pNewNode->Previous() = pWhere;
		pWhere->Next()->Previous() = pNewNode;
		pWhere->Next() = pNewNode;

		return XN_STATUS_OK;
	}


	/** The base node for the list */
	XnNode* m_pBase;
	
	INiNodeAllocator* m_pNodeAllocator;
	XnBool m_bOwnsAllocator;

private:
	XN_DISABLE_COPY_AND_ASSIGN(XnList);
};

/**
* Declares a list of type @a Type, which is named @a ClassName. The list uses translator @a Translator,
* and is declared using the @a decl declspec.
*/
#define XN_DECLARE_LIST_WITH_TRANSLATOR_DECL(decl, Type, ClassName, Translator)					\
	class decl ClassName : public XnList														\
	{																							\
	public:																						\
		class decl ConstIterator : public XnList::ConstIterator									\
		{																						\
		public:																					\
			friend class ClassName;																\
			inline ConstIterator(const ConstIterator& other) : XnList::ConstIterator(other) {}	\
			inline ConstIterator& operator++()													\
			{																					\
				++(*(XnList::ConstIterator*)this);												\
				return (*this);																	\
			}																					\
			inline ConstIterator operator++(int)												\
			{																					\
				ConstIterator result = *this;													\
				++*this;																		\
				return result;																	\
			}																					\
			inline ConstIterator& operator--()													\
			{																					\
				--(*(XnList::ConstIterator*)this);												\
				return (*this);																	\
			}																					\
			inline ConstIterator operator--(int)												\
			{																					\
				ConstIterator result = *this;													\
				--*this;																		\
				return result;																	\
			}																					\
			inline Type const& operator*() const												\
			{																					\
				return Translator::GetFromValue(**((XnList::ConstIterator*)this));				\
			}																					\
			inline Type const* operator->() const { return (&**this); }							\
		protected:																				\
			inline ConstIterator(XnNode* pNode) : XnList::ConstIterator(pNode) {}				\
			inline ConstIterator(const XnList::ConstIterator& other) :							\
				XnList::ConstIterator(other)  													\
			{}																					\
		};																						\
		class decl Iterator : public ConstIterator												\
		{																						\
		public:																					\
			friend class ClassName;																\
			Iterator(const Iterator& other) : ConstIterator(other) {}							\
			inline Iterator& operator++()														\
			{																					\
				++(*(ConstIterator*)this);														\
				return (*this);																	\
			}																					\
			inline Iterator operator++(int)														\
			{																					\
				Iterator result = *this;														\
				++*this;																		\
				return result;																	\
			}																					\
			inline Iterator& operator--()														\
			{																					\
				--(*(ConstIterator*)this);														\
				return (*this);																	\
			}																					\
			inline Iterator operator--(int)														\
			{																					\
				Iterator result = *this;														\
				--*this;																		\
				return result;																	\
			}																					\
			inline Type& operator*() const { return ((Type&)**(ConstIterator*)this); }			\
			inline Type* operator->() const { return (&**this); }								\
		protected:																				\
			inline Iterator(XnNode* pNode) : ConstIterator(pNode) {}							\
			inline Iterator(const XnList::Iterator& other) : ConstIterator(other) {}			\
		};																						\
	public:																						\
		ClassName()																				\
		{																						\
		}																						\
		~ClassName()																			\
		{																						\
			while (!IsEmpty())																	\
				Remove(begin());																\
		}																						\
		inline XnStatus AddFirst(Type const& value)												\
		{																						\
			XnValue val = Translator::CreateValueCopy(value);									\
			XnStatus nRetVal = XnList::AddFirst(val);											\
			if (nRetVal != XN_STATUS_OK)														\
			{																					\
				Translator::FreeValue(val);														\
				return (nRetVal);																\
			}																					\
			return XN_STATUS_OK;																\
		}																						\
		inline XnStatus AddLast(Type const& value)												\
		{																						\
			XnValue val = Translator::CreateValueCopy(value);									\
			XnStatus nRetVal = XnList::AddLast(val);											\
			if (nRetVal != XN_STATUS_OK)														\
			{																					\
				Translator::FreeValue(val);														\
				return (nRetVal);																\
			}																					\
			return XN_STATUS_OK;																\
		}																						\
		inline XnStatus AddAfter(ConstIterator where, Type const& value)						\
		{																						\
			XnValue val = Translator::CreateValueCopy(value);									\
			XnStatus nRetVal = XnList::AddAfter(where, val);									\
			if (nRetVal != XN_STATUS_OK)														\
			{																					\
				Translator::FreeValue(val);														\
				return (nRetVal);																\
			}																					\
			return XN_STATUS_OK;																\
		}																						\
		inline XnStatus AddBefore(ConstIterator where, Type const& value)						\
		{																						\
			XnValue val = Translator::CreateValueCopy(value);									\
			XnStatus nRetVal = XnList::AddBefore(where, val);									\
			if (nRetVal != XN_STATUS_OK)														\
			{																					\
				Translator::FreeValue(val);														\
				return (nRetVal);																\
			}																					\
			return XN_STATUS_OK;																\
		}																						\
		inline ConstIterator Find(Type const& value) const										\
		{																						\
			XnValue _value = Translator::GetAsValue(value);										\
			return XnList::Find(_value);														\
		}																						\
		inline Iterator Find(Type const& value)													\
		{																						\
			XnValue _value = Translator::GetAsValue(value);										\
			return XnList::Find(_value);														\
		}																						\
		inline XnStatus Remove(ConstIterator where)												\
		{																						\
			XnValue val = Translator::GetAsValue(*where);										\
			XnStatus nRetVal = XnList::Remove(where);											\
			if (nRetVal != XN_STATUS_OK) return (nRetVal);										\
			Translator::FreeValue(val);															\
			return XN_STATUS_OK;																\
		}																						\
		inline XnStatus Remove(Type const& value)												\
		{																						\
			Iterator it = Find(value);															\
			return Remove(it);																	\
		}																						\
		inline Iterator begin() { return XnList::begin(); }										\
		inline ConstIterator begin() const { return XnList::begin(); }							\
		inline Iterator end() { return XnList::end(); }											\
		inline ConstIterator end() const { return XnList::end(); }								\
		inline Iterator rbegin() { return XnList::rbegin(); }									\
		inline ConstIterator rbegin() const { return XnList::rbegin(); }						\
		inline Iterator rend() { return XnList::rend(); }										\
		inline ConstIterator rend() const { return XnList::rend(); }							\
	protected:																					\
		virtual XnStatus Remove(XnList::ConstIterator where)									\
		{																						\
			return Remove(ConstIterator(where));												\
		}																						\
	private:																					\
		XN_DISABLE_COPY_AND_ASSIGN(ClassName);													\
	};

/**
* Declares a list of type @a Type, which is named @a ClassName. The list uses translator @a Translator.
*/
#define XN_DECLARE_LIST_WITH_TRANSLATOR(Type, ClassName, Translator)							\
	XN_DECLARE_LIST_WITH_TRANSLATOR_DECL(, Type, ClassName, Translator)

/**
* Declares a list of type @a Type, which is named @a ClassName. The list uses creates a default translator
* and is declared using the @a decl declspec.
*/
#define XN_DECLARE_LIST_DECL(decl, Type, ClassName)														\
	XN_DECLARE_DEFAULT_VALUE_TRANSLATOR_DECL(decl, Type, XN_DEFAULT_TRANSLATOR_NAME(ClassName))			\
	XN_DECLARE_LIST_WITH_TRANSLATOR_DECL(decl, Type, ClassName, XN_DEFAULT_TRANSLATOR_NAME(ClassName))

/**
* Declares a list of type @a Type, which is named @a ClassName. The list uses creates a default translator.
*/
#define XN_DECLARE_LIST(Type, ClassName)		\
	XN_DECLARE_LIST_DECL(, Type, ClassName)
																							
#endif // _XN_LIST_H																		
																						
