/**
 * @fileoverview BWin Relational tree STL wrapper
 *
 * <pre>
 * Copyright(c) 2003-2007 by BWin Technologies Ltd.
 * http://www.bwintech.com
 * Quay House, South Esplanade, St. Peter Port
 * GY1 4EJ Guernsey, CI, UK
 * All rights reserved.
 * </pre>
 */

var RelationalTree = Class.create(function(container){
	if(!container){
		TRACE(0, "RTree: Unable to build relational tree, no container was given.");
		return;
	}else if(container.bMultiContainer){
		TRACE(0, "RTee: A multi container is not alloud for the relational tree.");
		return;
	}else if(
			container.find.isType() != "function"	||
			container.insert.isType() != "function"	||
			container.erase.isType() != "function"
		){
		TRACE(0, "RTree: Unable to build relational tree, container did not match STL specifications.");
		return;
	}

	this.container = container;
}); // Class.create(RelationalTree)

Object.extend(RelationalTree.prototype, {
	// Wrap find, and always return the .item part. We don't want to mess with the underlying object.
	find: function(arg){ return this.container.find(arg).item.second; },

	// Insert gets two arguments, parent key and the node wich has to be appended there.
	insert: function(parentKey, relNode){
		// Check some stuff to make sure we are not going to blow up tree
		if(parentKey === undefined || parentKey === null){
			TRACE(0, "RTree.insert: No parent key given during insert. [" + (relNode ? relNode.second.nodeId : "null") +"]");
			return;
		}else if(!relNode){
			TRACE(0, "RTree.insert: No relational node given to insert.");
			return;
		}else if(relNode.second.nodeId === undefined || relNode.second.nodeId === null){
			TRACE(0, "RTree.insert: Node did not contain a nodeId property.");
			return;
		}else if ( parentKey == relNode.second.nodeId ){
			TRACE(0, "RTree.insert: We do not tolerate hermaphrodites / incest.");
			return;
		}else if(relNode.second.nodeId !== relNode.first){
			// Just adding a warning here this can save you hours of time in case of a typo
			TRACE(0, "RTree.insert: Warning: Relatational nodeId and first don't match :: [" + relNode.first.toString() + "][" + relNode.second.nodeId + "]");
			TRACE(0, "                       Fix this as soon as possible it can cause some nasty bugs in your code.");
		}

		// Insert the relational node in the container, and gives us a pointer to the relational node.
		// we don't want to mess with the private node of the container.
		this.container.insert(relNode);
		var parentNode = this.find(parentKey);

		// We are not going to search for relNode.second.nodeId here. In the case they don't match we want to
		// know for sure that we actually do find something...
		var insertedNode = this.find(relNode.first);

		// Handle the relations.
		if(parentNode){
			// Just making sure it is null and stays that way.
			insertedNode.nextSibling = null;

			// Check if we are the first child of the particular parent in question.
			if(!parentNode.lastChild){
				// Just adding a little integrity check.
				if(parentNode.firstChild){
					TRACE(0, "RTree.insert: Node [" + parentNode.nodeId + "] has been corrupted in some way. Continueing to use this tree is not recommended."); }

				// Because last child was not set this has to be the first child node of this node. Set both first and last child.
				parentNode.firstChild = insertedNode.nodeId;
				parentNode.lastChild = insertedNode.nodeId;
			}else{
				// We were not the first one to be appended as children on the parent node
				insertedNode.prevSibling = parentNode.lastChild;
				this.find(parentNode.lastChild).nextSibling = insertedNode.nodeId;
				parentNode.lastChild = insertedNode.nodeId;
			}
		}else{
			// Somehow there was not parent node available at the time.... So just pop a warning and continue.
			TRACE(0, "RTree.insert: ParentNode [" + parentKey + "] was not found, No relations set.");
		}

		return insertedNode;
	},

	// Move one node from one parent to another.
	move: function(targetNode, newParentNode){
		var tNode = this.find(targetNode);
		var newPNode = this.find(newParentNode);

		// check if both objects excist in the tree.
		if(!tNode || tNode.nodeId === undefined || tNode.nodeId === null){
			TRACE(0, "RTree.move: No target node was given to move.");
			return;
		}else if(!newPNode || newPNode.nodeId === undefined || newPNode.nodeId === null){
			TRACE(0, "RTree.move: No new parent was given to move.");
			return;
		} else if(newPNode.nodeId == tNode.nodeId){
			TRACE(0, "RTree.move: You can not move a node unto itself.");
			return;
		}

		// we passed the test so apperently we have two very real nodes here...
		var parentNode = this.find(tNode.parentNode);

		// We are the only child in this node.
		if(!tNode.prevSibling && !tNode.nextSibling){
				parentNode.firstChild = null;
				parentNode.lastChild = null;

		// We are the first child of this node.
		}else if(!tNode.prevSibling){
			parentNode.firstChild = tNode.nextSibling;

		// We are the last child of this node.
		}else if(!tNode.nextSibling){
			parentNode.lastChild = tNode.prevSibling;

		// We are some node in the middle
		}else{
			this.find(tNode.prevSibling).nextSibling = tNode.nextSibling;
			this.find(tNode.nextSibling).prevSibling = tNode.prevSibling;
		}

		return this.insert(newPNode.nodeId, tNode);
	},

	// Change a node location within the parent.
	swap: function(sourceKey, targetKey){
		var sourceNode = this.find(sourceKey);
		var targetNode = this.find(targetKey);
		var parentNode = this.find(sourceNode.parent);

		// Do a check up, check if we are not going to swap some illegal node stuff etc.
		if(!sourceNode || sourceNode.nodeId === undefined || sourceNode.nodeId === null){
			TRACE(0, "RTree.swap: Unable to swap nodes source [" + sourceKey + "] is not a node.");
			return;
		}else if(!targetNode || targetNode.nodeId === undefined || sourceNode.nodeId === null){
			TRACE(0, "RTree.swap: Unable to swap nodes target [" + targetKey + "] is not a node.");
			return;
		}else if(sourceNode.parentNode != targetNode.parentNode){
			TRACE(0, "RTree.swap: Parent id's were not the same unable to swap nodes.");
			return;
		// In the HIGHLY unlikely chance something realy realy wierd is going on
		}else if(!sourceNode.prevSibling && !sourceNode.nextSibling){
			TRACE(0, "RTree.swap: Nodes can not be swapped because prevSibling and nextSibling of the source node are invalid.");
			return;
		}else if(!targetNode.prevSibling && !targetNode.nextSibling){
			TRACE(0, "RTree.swap: Nodes can not be swapped because prevSibling and nextSibling of the target node are invalid.");
			return;
		}else if(parentNode.firstChild == parentNode.lastChild){
			TRACE(0, "RTree.swap: Nodes can not be swapped because there is only one child in this parent.");
			return;
		}

		// We are done cheching stuff now lets start swapping...
		if(sourceNode.prevSibling){
			this.find(sourceNode.prevSibling).nextSibling = targetNode.nodeId;
		}else{
			parentNode.firstChild = targetNode.nodeId;
		}

		if(sourceNode.nextSibling){
			this.find(sourceNode.nextSibling).prevSibling = targetNode.nodeId;
		}else{
			parentNode.lastChild = targetNode.nodeId;
		}

		if(targetNode.prevSibling){
			this.find(targetNode.prevSibling).nextSibling = sourceNode.nodeId;
		}else{
			this.find(targetNode.parent).firstChild = sourceNode.nodeId;
		}

		if(targetNode.nextSibling){
			this.find(targetNode.nextSibling).prevSibling = sourceNode.nodeId;
		}else{
			this.find(targetNode.parent).lastChild = sourceNode.nodeId;
		}
	},

	// Gets all the children from a particular node.
	getChildren: function(parentKey){
		var parentNode = this.find(parentKey);
		var childrenArray = [];

		if(!parentNode){
			TRACE(0, "RTree.getChildren: Unable to get children for [" + parentKey + "] is not a node.");
			return;
		}

		// Check if the parent even has children
		if(!parentNode.firstChild){
			// Add a integrytie check here
			if(parentNode.lastChild){
				TRACE(0, "RTree.getChildren: The tree has been corrupeted continueing to use this tree is not reccomended."); }
			return childrenArray;
		}

		// Do the first one by hand then put it in the loop...
		var currentNode = this.find(parentNode.firstChild);
		childrenArray.push(currentNode.nodeId);

		// Loop through all the child nodes.
		while(currentNode.nextSibling){
			currentNode = this.find(currentNode.nextSibling);
			childrenArray.push(currentNode.nodeId);
		}

		return childrenArray;
	},

	// Remove the node...
	erase: function(nodeKey){
		var node = this.find(nodeKey);

		if(!node){
			TRACE(0, "RTree.erase: Unable to find target node [" + nodeKey + "] to remove.");
			return;
		}

		// So now we have a node we have to remove. We always remove the subtree (children) aswell, if
		// the sub tree is very large this could become quite costly...
		var nodeChildren = this.getChildren(node.nodeId);
		var self = this; // For the inner function of each()

		// We have all the children in the bag, add some bricks and chuck it in the river.
		nodeChildren.each(function(obj, indx, arr){
			self.erase(arr[indx]);
		});

		// And finally remove the node itself.
		this.container.erase(nodeKey);
	},

	toChars: function(){
		return "{/*BLUB*/}";
	}
});


var RelNode = Class.create(function(key, data){
	// Check if a key was given, no key, no node...
	if(key === undefined || key === null){
		TRACE(0, "RNode: Unable to create node, no key was given.");
		return;
	}

	this.nodeId = key;

	Object.protectedExtend(this, data);
});
Object.extend(RelNode.prototype, {
	prevSibling:	null,
	nextSibling:	null,
	firstChild:		null,
	lastChild:		null,
	parent:			null,

	nodeId:	null,

	get: function(label){ return this[label] },

	set: function(label, data){
		if(this.constructer.prototype[label] !== undefined){
			TRACE(0, "RelNode.set: Unable to set item labeled [" + label + "], label already used in prototype.");
			return undefined;
		}

		this[label] = data;
		return this[label];
	}
});
