/*
Callbacks:

tree.nodeMovedCallback(src, sourceParent, dest, where)
tree.nodeCreatedCallback( newone)
tree.selectionChangedCallback(curselect)
tree.nodeDeletedCallback(node)

*/

var TreeLib = function()
{
	return { 
		addNodes : function(tree, parent, nodes) 
		{
			for (var i = 0; i < nodes.length; i++) 
			{
				var obj = nodes[i];
				if (obj) 
				{
					var child = TreeLib.addNode(tree, parent, obj);
					if (obj.children) 
					{
						TreeLib.addNodes(tree, child, obj.children);
					}
				}
			}
		},
		
		ensureToggler : function(tree, node, state) 
		{
			if (node.open != state) 
			{
				TreeLib.toggleNode(tree, node);
			}
		},
		
		ensureVisible : function(tree, node)
		{
			while(node.parent != null)
			{
				TreeLib.ensureToggler(tree, node.parent, true);
				node = node.parent;
			}
		},
		
		toggleNode : function(tree, node) 
		{
			var openname = tree.names.open;
			if (!node.open) 
			{
				TreeLib.addHidden(tree, openname, node.attr("id"));
			} 
			else 
			{
				TreeLib.removeHidden(tree, openname, node.attr("id"));
			}
			node.open = !node.open;
			node.navchild.toggleClass("hidden");
			TreeLib.setupImages(tree, node);
			return false;
		},
		
		setupImages : function(tree, node) 
		{
			var togImage = tree.closedToggler;
			if (node.details.nokids) 
			{
				togImage = tree.hiddenToggler;
			} 
			else 
			{
				if (node.open) 
				{
					togImage = tree.openToggler;
				}
			}
			node.toggler.attr("src", togImage);
			if (node.details.icon) 
			{
				node.icon.attr("src", node.details.icon);
			}
		},
		
		updateTreeDisplay : function(tree, node) 
		{
			TreeLib.setupImages(tree, node);
			node.label.text(node.textvalue);
		},
		
		clickNode : function(tree, node) 
		{
			if (tree.nodeClickedCallback) 
			{
				return tree.nodeClickedCallback(node);
			}
			return TreeLib.defaultClickNode(tree, node);
		},
		
		defaultClickNode : function(tree, node) 
		{
			var curselect = tree.selected;
			if (curselect) 
			{
				curselect.label.toggleClass("selected");
			}
			if (curselect != node) 
			{
				node.label.toggleClass("selected");
				tree.selected = node;
				TreeLib.editHidden(tree, tree.names.selected, node.attr("id"));
				TreeLib.tryChange(tree, curselect)
			} 
			else 
			{
				TreeLib.removeSelection(tree);
			}
			return false;
		},
		
		tryChange : function(tree, curselect)
		{
			if (curselect != tree.selected)
			{
				if (tree.selectionChangedCallback)
				{
					tree.selectionChangedCallback(curselect);
				}
			}
		},
		
		removeSelection : function(tree)
		{
			var curselect = tree.selected;
			tree.selected = null;
			TreeLib.tryChange(tree, curselect);
			TreeLib.editHidden(tree, tree.names.selected, "");
		},
		
		removeHidden : function(tree, name, val) 
		{
			$("input:hidden[name=" + name + "][value=" + val + "]", tree.rootElement).remove();
		},
		
		editHidden : function(tree, name, val) 
		{
			var orig = $("input:hidden[name=" + name + "]", tree.rootElement);
			if (orig.length == 0) {
				TreeLib.addHidden(tree, name, val);
			} else {
				orig.val(val);
			}
		},
		
		addHidden : function(tree, name, val) 
		{
			return $('<input type="hidden" name="' + name + '" value="' + val + '">').appendTo(tree.rootElement);
		},
		
		addNode : function(tree, parent, details)
		{
			var newone = $("#sampleNode").clone(true);
			newone.details = details;
			newone.parent = parent;			
			newone.details.nokids = true;
			newone.tree = tree;
			newone.attr("id", details.uuid);
			newone.label = $(".label", newone);
			newone.toggler = $(".toggler", newone);
			newone.navchild = $(".navChildren", newone);
			newone.open = details.open;
			if (details.open) 
			{
				var openname = tree.names.open;
				newone.navchild.toggleClass("hidden");
				TreeLib.addHidden(tree, openname, details.uuid);
			}
			newone.tabs = details.tabs;
			var nodeLine = $(".nodeLine", newone);
			newone.nodeline = nodeLine;
			newone.icon = $(".icon", nodeLine);
			newone.text = $(".text", nodeLine);
			newone.link = $(".link", newone);
			newone.textvalue = details.name;
			newone.children = [];
			
			TreeLib._addChild(tree, parent, newone);
			
			TreeLib.initialiseNode(tree, newone);
			TreeLib.updateTreeDisplay(tree, newone);
		
			if (tree.nodeCreatedCallback) 
			{
				tree.nodeCreatedCallback(newone);
			}
			
		    newone.show();
			return newone;
		},
		
		/*
		 index: optional
		*/
		_addChild: function(tree, parent, node, index)
		{
			node.parent = parent;
			var navParent;
			if (!parent) 
			{
				navParent = tree.rootElement;
			} 
			else
			{
				navParent = parent.navchild;
			}
			navParent.append(node);
			
			var siblingList = TreeLib.getSiblings(tree, node);
			
			if (typeof(index) != 'undefined')
			{
				siblingList.splice(index, 0, node);
				// reshuffle child indexes...
				for (var i = 0; i < siblingList.length; i++)
				{
					siblingList[i].childIndex = i;
				}
			}
			else
			{
				node.childIndex = siblingList.length;
				siblingList.push(node);
			}
			
			if (parent)
			{
				parent.details.nokids = false;
				TreeLib.setupImages(tree, parent);
			}
		},
		
		_removeChild: function(tree, parent, node)
		{
			var siblingList = TreeLib.getSiblings(tree, node);
			
			siblingList.splice(node.childIndex, 1);
			// reshuffle child indexes...
			for (var i = 0; i < siblingList.length; i++)
			{
				siblingList[i].childIndex = i;
			}
			
			if (parent)
			{
				parent.details.nokids = (siblingList.length == 0);
				TreeLib.setupImages(tree, parent);
			}
		},
		
		initialiseNode: function(tree, node)
		{
			var pare = node.parent;
			if (pare)
			{
				pare.details.nokids = false;
				TreeLib.setupImages(tree, pare);
			}
			
			node.toggler.bind("click", node, 
				function(event) 
				{
					return TreeLib.toggleNode(tree, event.data);
				});
					
			node.link.bind("click", node, 
				function(event) 
				{
					return TreeLib.clickNode(tree, event.data);
				});
				
			TreeLib.ensureVisible(tree, node);
		},	
			
		getPrevNode : function(tree, node)
		{
			var siblings = TreeLib.getSiblings(tree, node);
			for (var i = 0; i < siblings.length; i++)
			{
				if (siblings[i] == node)
				{
					if (i > 0)
					{
						return siblings[i-1];
					}
					else
					{
						return null;
					}
				}
			}
			return null;
		},
		
		getNextNode : function(tree, node)
		{
			var siblings = TreeLib.getSiblings(tree, node);
			for (var i = 0; i < siblings.length; i++)
			{
				if (siblings[i] == node)
				{
					if (i < siblings.length - 1)
					{
						return siblings[i+1];
					}
					else
					{
						return null;
					}
				}
			}
			return null;
		},
		
		initTree : function(tree) 
		{
			tree.nextNumber = 0;
		},
		
		getSiblings : function(tree, node)
		{
			if (!node.parent) 
			{
				return tree.rootNodes;
			} 
			else 
			{
				return node.parent.children;
			}
		},
		
		deleteNode : function(tree, node)
		{
			if (node)
			{
				while (node.children.length > 0)
				{
					TreeLib.deleteNode(tree, node.children[0]);
				}
				
				if (tree.nodeDeletedCallback)
				{
					tree.nodeDeletedCallback(node);
				}
				
				var id = node.attr("id");
				TreeLib.addHidden(tree, tree.names.deleted, id);
				if (tree.selected == node)
				{
					TreeLib.removeSelection(tree);
				}
				TreeLib._removeChild(tree, node.parent, node);
				node.remove();
			}
		},
		
		moveNode : function(tree, src, dest, where)
		{
			// sanity check
			if (src == dest || !dest)
			{
				return;
			}
			
			// check src is not higher up in the destination tree.  i.e. you can't make a node a child of itself.
			var pare = dest.parent;
			while (pare != null)
			{
				if (pare == src)
				{
					return;
				}
				pare = pare.parent;
			}
			
			var sourceParent = src.parent;
			
			if (where == 'on')
			{
				TreeLib._removeChild(tree, sourceParent, src);
				TreeLib._addChild(tree, dest, src);
				
				dest = null;
			}
			
			else if (where == 'before')
			{
				TreeLib._removeChild(tree, sourceParent, src);
				TreeLib._addChild(tree, dest.parent, src, dest.childIndex);
				
				src.parent = dest.parent;
				dest.before(src);
			}
			
			else if (where == 'after')
			{
				TreeLib._removeChild(tree, sourceParent, src);
				TreeLib._addChild(tree, dest.parent, src, dest.childIndex + 1);
				
				src.parent = dest.parent;
				dest.after(src);
			}
			
			TreeLib.ensureVisible(tree, src);
			
			if (tree.nodeMovedCallback)
			{
				tree.nodeMovedCallback(src, sourceParent, dest, where);
			}
		},
		
		tabEncoder : function(tree, tab) 
		{
			var val = escape(Utf8.encode(tab.name));
			val += '|';
			val += escape(Utf8.encode(tab.viewer));
			val += '|';
			var attach = tab.attachment;
			if (attach == null)
			{
				attach = '';
			}
			val += escape(Utf8.encode(attach));
			return val;
		}
	};
}();

