// Adds helper functionalities for the o3d.RenderNode objects
//

o3djs.require('patapi.base');


patapi.RenderNode = 
{
	// Inserts a RenderNode after the specified node
	//	_ParentNode, the parent node where the new child node will be inserted
	//	_CurrentNode, the node after which to insert
	//	_NodeToInsert, the node to insert
	//
	InsertAfter : function( _ParentNode, _CurrentNode, _NodeToInsert )
	{
		// Shift all sibling nodes' priorities
		var Siblings = _ParentNode.children;
		for ( var SiblingIndex=0; SiblingIndex < Siblings.length; SiblingIndex++ )
		{
			var	Sibling = Siblings[SiblingIndex];
			if ( Sibling.priority > _CurrentNode.priority )
				Sibling.priority++;
		}

		// Insert the node
		_NodeToInsert.parent = _ParentNode;
		_NodeToInsert.priority = _CurrentNode.priority + 1;
	},

	// Inserts a RenderNode before the specified node
	//	_ParentNode, the parent node where the new child node will be inserted
	//	_CurrentNode, the node before which to insert
	//	_NodeToInsert, the node to insert
	//
	InsertBefore : function( _ParentNode, _CurrentNode, _NodeToInsert )
	{
		// Shift all sibling nodes' priorities, including the current node
		var Siblings = _ParentNode.children;
		for ( var SiblingIndex=0; SiblingIndex < Siblings.length; SiblingIndex++ )
		{
			var	Sibling = Siblings[SiblingIndex];
			if ( Sibling.priority >= _CurrentNode.priority )
				Sibling.priority++;
		}

		// Insert the node
		_NodeToInsert.parent = _ParentNode;
		_NodeToInsert.priority = _CurrentNode.priority;
	},

	// Adds a RenderNode as a child of the specified node
	//	_ParentNode, the parent node to add a child to
	//	_NewChildNode, the new child node to add
	//
	AddChild : function( _ParentNode, _NewChildNode )
	{
		// Retrieve the highest priority
		var	MaxPriority = -1;
		var Children = _ParentNode.children;
		for ( var ChildIndex=0; ChildIndex < Children.length; ChildIndex++ )
		{
			var	Child = Children[ChildIndex];
			MaxPriority = Math.max( MaxPriority, Child.priority );
		}

		// Add the child node
		_NewChildNode.parent = _ParentNode;
		_NewChildNode.priority = MaxPriority + 1;
	},

	// Detaches a node from the hierarchy of RenderNodes
	//	_Node, the node to detach
	//
	Detach : function( _Node )
	{
		_Node.parent = null;
	},

	// Moves the current node's priority to the specified priority
	//	_ParentNode, the parent node
	//	_Node, the node to move
	//	_NewPriority, the new priority for the node
	//
	Move : function( _ParentNode, _Node, _NewPriority )
	{
		// Translate all nodes inbetween current and new priority
		var MinPriority, MaxPriority, Add;
		if ( _Node.priority < _NewPriority )
		{
			MinPriority = _Node.priority + 1;
			MaxPriority = _NewPriority;
			Add = -1;	// Scroll to the left
		}
		else if ( _Node.priority > _NewPriority )
		{
			MinPriority = _NewPriority;
			MaxPriority = _Node.priority - 1;
			Add = +1;	// Scroll to the right
		}
		else
			return;	// Same priorities, don't do anything!

		// Shift all sibling nodes' priorities
		var Siblings = _ParentNode.children;
		for ( var SiblingIndex=0; SiblingIndex < Siblings.length; SiblingIndex++ )
		{
			var	Sibling = Siblings[SiblingIndex];
			if ( Sibling.priority >= MinPriority || Sibling.priority <= MaxPriority )
				Sibling.priority += Add;
		}

		// Change priority
		_Node.priority = _NewPriority;
	},

	// Recursively finds all the nodes of a given name
	//	_Node, the current node to start finding nodes from
	//	_Name, the name of the nodes to find
	//
	//	returns an array of RenderNodes of the specified name
	//
	FindByName : function( _Node, _Name )
	{
		if ( _Node == null )
			return	[];
			
		var Result = [];
		if ( _Node.name == _Name )
			Result.push( _Node );

		var Children = _Node.children;
		for ( var ChildIndex=0; ChildIndex < Children.length; ChildIndex++ )
		{
			var	Child = Children[ChildIndex];
			Result.concat( this.FindByName( Child, _Name ) );
		}

		return	Result;
	},

	// Recursively finds all the nodes of a given type
	//	_Node, the current node to start finding nodes from
	//	_ClassName, the type of the nodes to find
	//
	//	returns an array of RenderNodes of the specified type
	//
	FindByType : function( _Node, _ClassName )
	{
		if ( _Node == null )
			return	[];
			
		var Result = [];
		if ( _Node.className == _ClassName )
			Result.push( _Node );

		var Children = _Node.children;
		for ( var ChildIndex=0; ChildIndex < Children.length; ChildIndex++ )
		{
			var	Child = Children[ChildIndex];
			Result.concat( this.FindByType( Child, _Name ) );
		}

		return	Result;
	},

	// Recursively finds all the nodes of a given type and having the given name
	//	_Node, the current node to start finding nodes from
	//	_Name, the name of the nodes to find
	//	_ClassName, the type of the nodes to find
	//
	//	returns an array of RenderNodes of the specified type and name
	//
	FindByNameAndType : function( _Node, _Name, _ClassName )
	{
		if ( _Node == null )
			return	[];
			
		var Result = [];
		if ( _Node.name == _Name && _Node.className == _ClassName )
			Result.push( _Node );

		var Children = _Node.children;
		for ( var ChildIndex=0; ChildIndex < Children.length; ChildIndex++ )
		{
			var	Child = Children[ChildIndex];
			Result.concat( this.FindByNameAndType( Child, _Name, _ClassName ) );
		}

		return	Result;
	},

	// Recursively finds the FIRST node of a given name
	//	_Node, the current node to start finding nodes from
	//	_Name, the name of the nodes to find
	//
	//	returns the first match or null if not found
	//
	FindFirstByName : function( _Node, _Name )
	{
		if ( _Node == null )
			return	[];
			
		if ( _Node.name == _Name )
			return	_Node;

		var Children = _Node.children;
		for ( var ChildIndex=0; ChildIndex < Children.length; ChildIndex++ )
		{
			var	Child = Children[ChildIndex];

			var	Result = this.FindFirstByName( Child, _Name );
			if ( Result != null )
				return	Result;
		}

		return	null;
	},

	// Recursively finds the FIRST node of a given type
	//	_Node, the current node to start finding nodes from
	//	_ClassName, the type of the nodes to find
	//
	//	returns the first match or null if not found
	//
	FindFirstByType : function( _Node, _ClassName )
	{
		if ( _Node == null )
			return	[];

		if ( _Node.className == _ClassName )
			return	_Node;

		var Children = _Node.children;
		for ( var ChildIndex=0; ChildIndex < Children.length; ChildIndex++ )
		{
			var	Child = Children[ChildIndex];

			var	Result = this.FindFirstByType( Child, _ClassName );
			if ( Result != null )
				return	Result;
		}

		return	Result;
	},

	// Recursively finds the FIRST node of a given type and having the given name
	//	_Node, the current node to start finding nodes from
	//	_Name, the name of the nodes to find
	//	_ClassName, the type of the nodes to find
	//
	//	returns the first match or null if not found
	//
	FindFirstByNameAndType : function( _Node, _Name, _ClassName )
	{
		if ( _Node == null )
			return	[];
			
		if ( _Node.name == _Name && _Node.className == _ClassName )
			return	_Node;

		var Children = _Node.children;
		for ( var ChildIndex=0; ChildIndex < Children.length; ChildIndex++ )
		{
			var	Child = Children[ChildIndex];

			var	Result = this.FindFirstByNameAndType( Child, _Name, _ClassName );
			if ( Result != null )
				return	Result;
		}

		return	Result;
	}
};
