/*************************************************************************/
/*	File : XNTree.h												 */
/*	Author :  Aymeric Bard												 */	
/*																		 */	
/*	Virtools SDK 														 */	 
/*	Copyright (c) Virtools 2000, All Rights Reserved.					 */	
/*************************************************************************/
#ifndef NTREE_H

#define NTREE_H

#include "XHashFun.h"

/************************************************
Summary: Class representation of a N tree.

Remarks:
	The tree can contains different types on its internal nodes and on
	its leaf. The typename N represent the internal node type, and the
	typename L the leaf type.

Example:

class MyTrav
{
public:
	void TraverseNode(const int& a)
	{
		if (a == 4)
			int b = 0;
	}

	void TraverseLeaf(const float& a)
	{
		if (a == 4.0f)
			int b = 0;
	}
} myTrav;

void foo()
{
	XNTree<int,float> tree;
	XNTree<int,float>::INode* node = tree.InsertNode(NULL,4);
	tree.InsertLeaf(node,4.0f);
	tree.InsertLeaf(node,5.0f);
	tree.InsertLeaf(node,6.0f);
    
	tree.Traverse(NULL,myTrav);

	XNTree<int,float>::Leaf* lnode = tree.FindLeaf(NULL,6.0f);
	XNTree<int,float>::INode* inode = tree.FindNode(NULL,4);
}



See Also : XBinaryTree
************************************************/
template <class N, class L,class EqN = XEqual<N>, class EqL = XEqual<L> >
class XNTree
{
public:

// Inner Classes

	// Generic Struct of a Node
	class INode;
	
	class Node {

	friend class XNTree<N,L,EqN,EqL>;

	public:

		// node type enumeration
		enum Type
		{
			NODE,
			LEAF
		};

		INode*
		GetParent() const
		{
			return parent;
		}

		bool
		IsLeaf() const
		{
			return (type == LEAF);
		}

		// Ctor
		Node(Type iType):type(iType) {}

	protected:
		INode*			parent;
		Type			type;
	};

	class INode : public Node {

	friend class XNTree<N,L,EqN,EqL>;

	INode():Node(Node::NODE) {}

	public:
		int
		GetChildrenCount() const
		{
			return children.Size();
		}

		Node*
		GetChildren(int i) const
		{
			assert(i < children.Size());
			return children[i];
		}

		N& GetData()
		{
			return data;
		}

	private:
		XArray<Node*>	children;
		N				data;
	};

	class Leaf : public Node {

	friend class XNTree<N,L,EqN,EqL>;

	public:
		Leaf():Node(Node::LEAF) {}

		L& GetData()
		{
			return data;
		}
	private:
		L				data;
	};

// Methods


	// Dtor
	~XNTree()
	{
		Clear();
	}

	// Return the roots count
	int	GetRootsCount() const
	{
		return m_Roots.Size();
	}

	// Return the i th root
	Node*	GetRoots(const int i) const
	{
		assert(i < m_Roots.Size());
		return m_Roots[i];
	}

	template <class Trav>
	void				Traverse(Node* iRoot, Trav& iTraversal) const
	{
		if (!iRoot) { // From the roots
			
			for (Node** it = m_Roots.Begin(); it != m_Roots.End(); ++it) {
				_Traverse(*it,iTraversal);
			}
		} else { // from a specific subtree root

			_Traverse(iRoot,iTraversal);
		}

	}

	// Insert an internal Node into a tree
	INode*				InsertNode(INode* iRoot, const N& iData)
	{
		// We create the node to insert
		INode* n = AllocateInternalNode();
		n->parent	= iRoot;
		n->data		= iData;

		_InsertNode(iRoot,n);
		return n;
	}

	// Insert a leaf into the tree
	Leaf*				InsertLeaf(INode* iRoot, const L& iData)
	{
		// We create the node to insert
		Leaf* n		= AllocateLeaf();
		n->parent	= iRoot;
		n->data		= iData;

		_InsertNode(iRoot,n);
		return n;
	}

	// Find a Node in the substree defined by iRoot
	INode*				FindNode(Node* iRoot, const N& iData) const
	{
		if (!iRoot) { // we start at the root
			for (Node** it = m_Roots.Begin(); it != m_Roots.End(); ++it) {
				INode* i = FindNode(*it,iData);
				if (i) 
					return i;
			}

			return NULL;
		}

		if (iRoot->type != Node::NODE) // Can't find a node on a leaf
			return NULL;
		
		INode* inode = (INode*)iRoot;

		// Is it me ?
		EqN equal;
		if (equal(iData,inode->data))
			return inode;

		// Not found, we continue to the chidren
		for (Node** it = inode->children.Begin(); it != inode->children.End(); ++it) {
			INode* i = FindNode(*it,iData);
			if (i)
				return i;
		}

		// Really not found on this subtree
		return NULL;
	}

	// Find a Node in the children of iRoot
	INode*				FindDirectNode(Node* iRoot, const N& iData) const
	{
		if(!iRoot)
		{
			for(Node** it = m_Roots.Begin(); it != m_Roots.End(); ++it)
			{
				if(!(*it)->IsLeaf() && ((INode*)*it)->GetData()==iData)
					return (INode*)*it;
			}

			return NULL;
		}

		if (iRoot->type != Node::NODE) // Can't find a node on a leaf
			return NULL;
		
		INode* inode = (INode*)iRoot;

		EqN equal;
		for (Node** it = inode->children.Begin(); it != inode->children.End(); ++it)
		{
			//if(!(*it)->IsLeaf() && ((INode*)*it)->GetData()==iData)
			if(!(*it)->IsLeaf() && equal(((INode*)*it)->GetData(),iData))
				return (INode*)*it;
		}

		return NULL;
	}
	

	// Find a Leaf in the substree defined by iRoot
	Leaf*				FindLeaf(Node* iRoot, const L& iData) const
	{
		if (!iRoot) { // we start at the root
			for (Node** it = m_Roots.Begin(); it != m_Roots.End(); ++it) {
				Leaf* l = FindLeaf(*it,iData);
				if (l) 
					return l;
			}

			return NULL;
		}

		if (iRoot->type == Node::LEAF) { // Is it me ?
			Leaf* leaf = (Leaf*)iRoot;
			
			EqL equal;
			if (equal(iData,leaf->data))
				return leaf;

		} else { // Try the children
			INode* inode = (INode*)iRoot;

			// Not found, we continue to the chidren
			for (Node** it = inode->children.Begin(); it != inode->children.End(); ++it) {
				Leaf* l = FindLeaf(*it,iData);
				if (l)
					return l;
			}
		}

		// Really not found on this subtree
		return NULL;
	}

	// Find a Leaf in the chilren of iRoot
	Leaf*				FindDirectLeaf(Node* iRoot, const L& iData) const
	{
		if (!iRoot) { // we start at the root
			for (Node** it = m_Roots.Begin(); it != m_Roots.End(); ++it) {
				if((*it)->IsLeaf() && ((Leaf*)*it)->GetData()==iData)
					return (Leaf*)*it;
			}

			return NULL;
		}

		if (iRoot->type != Node::NODE) // Can't find a node on a leaf
			return NULL;

		INode* inode = (INode*)iRoot;

		for (Node** it = inode->children.Begin(); it != inode->children.End(); ++it)
		{
			if((*it)->IsLeaf() && ((Leaf*)*it)->GetData()==iData)
				return (Leaf*)*it;
		}

		return NULL;
	}

	// Insert a leaf into the tree
	void				MoveNode(Node* iNode, INode* iRoot)
	{
		assert(iNode);
		assert(iRoot->type == Node::NODE);

		if (iNode->parent) { // it was a child of something
			iNode->parent->children.Remove(iNode);
		} else {
			m_Roots.Remove(iNode);
		}

		iNode->parent = iRoot;
		_InsertNode(iRoot,iNode);
	}

	// Extract a node from the tree
	void				ExtractNode(Node* iNode)
	{
		assert(iNode);

		if (iNode->parent) { // it was a child of something
			iNode->parent->children.Remove(iNode);

			// Remove the parent
			iNode->parent = NULL;
		} else {
			m_Roots.Remove(iNode);
		}

	}

	// Clear the entire tree
	void				Clear()
	{
		// we delete all the roots
		for (Node** n = m_Roots.Begin(); n != m_Roots.End(); ++n)
			_DeleteNode(*n);

		// We clear the array
		m_Roots.Resize(0);
	}

	void				DeleteSubTree(Node* iRoot)
	{
		if (!iRoot) return;

		// We clear the node from the children of its parent

		if (iRoot->parent) { // it was a child of something
			iRoot->parent->children.Remove(iRoot);
		} else {
			m_Roots.Remove(iRoot);
		}
		
		// we delete the node and all its subnode
		_DeleteNode(iRoot);
	}

private:

// Methods
	void				_InsertNode(INode* iRoot, Node* iNode)
	{
		if (!iRoot) { // we insert at the root of the tree
			m_Roots.PushBack(iNode);
		} else { // we insert under an existing node
			iRoot->children.PushBack(iNode);
		}
	}

	void				_DeleteNode(Node* iRoot)
	{
		
		// Delete the children, if any
		if (iRoot->type == Node::NODE) { // If we are an intenral Node
			INode* inode = (INode*)iRoot;
			for (Node** n = inode->children.Begin(); n != inode->children.End(); ++n)
				_DeleteNode(*n);

			// delete the node
			ReleaseInternalNode(inode);

		} else { // It's a leaf

			// delete the leaf
			ReleaseLeaf((Leaf*)iRoot);
		}

	}

	template <class Trav>
	void				_Traverse(Node* iRoot, Trav& iTraversal) const
	{
		assert(iRoot); // Should not be called with a NULL root

		if (iRoot->type == Node::NODE) { // Internal Node

			INode* inode = (INode*)iRoot;
			
			// The processing function call
			iTraversal.TraverseNode(inode->data);

			// continue to the children
			for (Node** it = inode->children.Begin(); it != inode->children.End(); ++it)
				_Traverse(*it,iTraversal);

			// The processing function call
			iTraversal.ExitNode(inode->data);

		} else { // leaf

			iTraversal.TraverseLeaf(((Leaf*)iRoot)->data);
		}
	}

	INode*				AllocateInternalNode()
	{
		return new INode;
	}
	
	Leaf*				AllocateLeaf()
	{
		return new Leaf;
	}

	void				ReleaseInternalNode(INode* iNode)
	{
		delete iNode;
	}
	
	void				ReleaseLeaf(Leaf* iLeaf)
	{
		delete iLeaf;
	}

// Members
	XArray<Node*>		m_Roots;
};


#endif // NTREE_H