/* $Id: tree.js,v 1.25 2011-11-25 08:58:47 lan Exp $ */
/*
 * Repository-tree related functions.
 */
var treeMap = {};
var treeActions;
var maxItemsToLoad = 50;

$(function() {$(document).bind("mousedown", function(e) {
	if(!$(e.target).closest(".tree-context").length)
	{
		for(var root in treeMap)
			treeMap[root].hide_context();
	}
});});

/*
 * Init repository tabs
 */
function initRepositoryTabs()
{
	$('#repositoryTabs').tabs({
		select: function(event, ui)
		{
			var tabId = ui.panel.id;
            tabId = tabId.substring(0, tabId.length - 15);
            if(!treeMap[tabId]) return;
            if (treeMap[tabId].selected) 
            {
                var path = getTreeNodePath(treeMap[tabId].selected[0]);
                storeLastOpenPath(path);
            }
            activeDC = rootMap[tabId];
		}
	});
	
	//$('#repositoryTabs').tabs('paging', { cycle: true, followOnSelect: true, selectOnAdd: false });

	updateRepositoryTabs();
}

function updateRepositoryTabs()
{
	for(var root in treeMap)
	{
		treeMap[root].destroy();
		delete rootMap[root].treeObj;
	}
	while($('#repositoryTabs').tabs('length') > 0)
		$('#repositoryTabs').tabs('remove', 0);
	treeMap = {};
	//add repository tabs
	for(var i in perspective.repository)
	{
		var root = perspective.repository[i].path;
		if(!rootMap[root]) rootMap[root] = new DataCollection(root);
		$('#repositoryTabs').append('<div id="'+root+'_tree_container" class="tree_container"><div id="'+root+'_tree"><ul><li id="#rt_'+root+'" class="open"><a href="#">'+root+'</a></li></ul></div></div>');
		$('#repositoryTabs').tabs('add', getJQueryIdSelector(root+'_tree_container'), perspective.repository[i].title);
		treeMap[root] = $.tree_create();
		initTree(treeMap[root], $(getJQueryIdSelector(root+"_tree")), rootMap[root]);
		rootMap[root].treeObj = treeMap[root];
	}
	$("#leftTopPane").trigger("resize");
}

/*
 * Init repository tree
 */
function initTree(treeObject, divObject, rootDC)
{
    treeObject.init(divObject, 
    {
        callback: 
        {
			onopen: function(node, treeObj)
			{
				if(treeObject.creatingItem) return;
				var path = getTreeNodePath(node);
				if(!isTreeDummyExists(path)) return;
				var dc = getTargetCollection(path);
				if(dc.treeLoading) return;
				dc.treeLoading = true;
				dc.getSize(new LoadNameListCallback(treeObject, node, dc).func);
                treeObj.select_branch(node);
			},
			onclose: function(node, treeObj)
			{
				if(treeObject.creatingItem) return;
				$(node).children("ul").remove();
				var path = getTreeNodePath(node);
				createTreeDummy(path);
				getTargetCollection(path).invalidateCollection();
			},
            onselect: function(node, treeObj)
            {
                setTimeout(function()
                {
                    if (node.id.indexOf("#rt_") == 0) 
                    {
						var path = getTreeNodePath(node);
						if(activeDC)
							activeDC.selectedElement = path;
                        storeLastOpenPath(path);
						path = getTargetPath(path); 
                        showElementInfo(path);
                        loadElementDescription(path);
                    }
                    else 
                        if (node.id.indexOf("#et_") == 0) 
                        {
                            var path = node.id.substring(4);
                            var dc = getTargetCollection(path);
                            
                            var parentNode = getTreeNode(path);
                            
                            var size = treeObject.children(parentNode).length - 1;
                            
                            dc.getElementInfoRange(size, size + maxItemsToLoad, new AddNameListCallback(treeObject, parentNode, dc, size).func);
                        }
                }, 300);
            },
            ondblclk: function(node, treeObj)
            {
				// treeObj.toggle_branch(node);
				var path = getTargetPath(getTreeNodePath(node));
				if(path)
                    openDocument(path);
            }
        },
        ui: 
        {
            context: []
        }
    });
    
    // init databases list
    rootDC.getNameList(function(nameList)
    {
    	createTreeElements(rootDC.getName(), nameList);
    });
	
	eventHolder.bind("actionsLoaded", function() {
        // update context menu
    	treeObject.settings.ui.context = treeActions;
    	treeObject.context_menu();
	});
}

function initTreeActions()
{
    // load actions
	if(!treeActions)
	{
		treeActions = [];
	    queryBioUML("web/action", 
	    {
	        type: "tree"
	    }, function(data)
	    {
            var actions = data.actions;
            for (i = 0; i < actions.length; i++) 
            {
                var actionProperties = new Action();
                actionProperties.parse(actions[i]);
				treeActions.push(actionProperties);
            }
			eventHolder.trigger("actionsLoaded");
	    });
	} else eventHolder.trigger("actionsLoaded");
}

/**
 * Returns tree object by given element path
 * 
 * @param {String}
 *            element path like "databases/Biopath" or "data/microarray"
 * @return corresponding tree object
 */
function getTreeObject(element)
{
	if(!element) return;
	var departs = getPathComponents(element);
	return treeMap[departs[0]] || treeMap["databases"];
}

/**
 * Returns DOM-element of the tree by given repository path If element is not
 * exists or not loaded, it will return undefined without trying to load it
 * 
 * @param String
 *            path - path to the element
 * @return DOMElement or undefined
 * @see getTreeNodePath
 */
function getTreeNode(path)
{
	return document.getElementById("#rt_"+path);
}

/**
 * Returns repository path associated with given tree node Returns undefined if
 * specified DOM-element is not tree node
 * 
 * @param DOMElement
 *            node
 * @return String containing repository path, undefined if error
 * @see getTreeNode
 */
function getTreeNodePath(node)
{
	if(node != undefined && node.id && (node.id.indexOf('#rt_') == 0))
	{
		return node.id.substring(4);
	}
	return undefined;
}

/**
 * Update content of given tree branch (all elements will be removed and loaded
 * again)
 * 
 * @param element -
 *            complete DataCollection path (like "data/SQLTracks") to refresh.
 *            Use activeDC.selectedElement for selected element
 * @param useParent - if true and tree element is not found, try to refresh parent's branch first
 * @param noBackLinks - if false, will try to refresh all the known collections linking to supplied as well
 */
function refreshTreeBranch(element, useParent, noBackLinks)
{
	if(!element) return;
	if(!noBackLinks)
	{
		var backLinkList = getBackPaths(element);
		for(var i=0; i<backLinkList.length; i++)
			refreshTreeBranch(backLinkList[i], useParent, true);
		return;
	}
    var dc = getDataCollection(element);
	dc.invalidateCollection();
	var elementObj = getTreeNode(element);
    if (!elementObj && useParent) 
    {
        refreshTreeBranch(getElementPath(element), useParent);
        elementObj = getTreeNode(element);
    }
	if(!elementObj || isTreeDummyExists(element)) return;
	treeObj = getTreeObject(element);
	var size = $(elementObj).children("ul").children("li").length;
	size = Math.ceil(Math.max(size-1,1)/maxItemsToLoad)*maxItemsToLoad;
	$(elementObj).children("ul").remove();
	dc.invalidateCollection();
	dc.getSize(new LoadNameListCallback(treeObj, elementObj, dc, size).func);
    treeObj.select_branch(elementObj);
}

/**
 * close tree branch
 * 
 * @param element -
 *          complete DataCollection path (like "data/SQLTracks") to refresh.
 *          Use activeDC.selectedElement for selected element
 */

function closeTreeBranch(element)
{
	var elementObj = document.getElementById("#rt_" + element);
	if(!elementObj || isTreeDummyExists(element)) return;
	treeObj = getTreeObject(element);
	treeObj.close_branch(elementObj);
}

function createTreeDummy(path)
{
	var parent = getTreeNode(path);
	var tree = getTreeObject(path);
	tree.creatingItem = true;
	var dummy = tree.create({
		attributes: {
			id: ("#rt_dummy!"+path),
			rel: "disabled"
		},
		data: {
			title: resources.treeLoading
		}
	}, parent);
	dummy.addClass("treeitem-disabled dummy");
	dummy.children("a").get(0).style.backgroundImage = "url('icons/busy.png')";
	tree.close_branch(parent, true);
	tree.creatingItem = false;
	return dummy;
};

/*
 * Note that this function heavily relies on internal jstree structure (though
 * it's much faster than using tree.create)
 */
function createTreeElements(parentPath, names)
{
	var elements = $();
	var tree = getTreeObject(parentPath);
	var parentNode = $(getTreeNode(parentPath));
	var dc = getTargetCollection(parentPath);
	tree.creatingItem = true;
	for(var i = 0; i < names.length; i++)
	{
		var title = names[i].name;
		if(names[i].title)
		{
			title = names[i].title;
		}
		var element = $(tree.parseJSON({
		        attributes: {
	            id: "#rt_"+createPath(parentPath, names[i].name)
	        },
	        data: {
	            title: title.escapeHTML()
	        }
	    }));
        
        if( activeDocumentId && opennedDocuments[activeDocumentId] && opennedDocuments[activeDocumentId].completeName 
            && opennedDocuments[activeDocumentId].completeName == createPath(parentPath, names[i].name) )
            element.addClass("active_node");
            
		if(names[i].hasChildren)
		{
			var dummy = $('<ul><li rel="disabled" class="leaf last treeitem-disabled dummy"><a class="" style="background-image: url(\'icons/busy.png\');" href="#">'+resources.treeLoading+'</a></li></ul>');
			dummy.find('li').get(0).id='#rt_dummy!'+createPath(parentPath, names[i].name);
			element.append(dummy);
		}
		var icon = getNodeIcon(dc, names[i].name);
	    if (icon != null) 
	        element.children("a").get(0).style.backgroundImage = icon;
	    element.children("a").eq(0).draggable({
			appendTo: "body",
			distance: 10,
			revert: "invalid",
			scope: "treeItem",
			helper: function(event) {
	    		return $(this).clone().css({"background-repeat": "no-repeat", "padding-left": "20px", "font-family": "verdana", "font-size": "11px", "text-decoration": "none", "height": "16px"
	    	});}
		});
		elements = elements.add(element);
	}
	if(elements.length > 0) {
		parentNode.children("ul").children("li.dummy").each(function()
		{
			tree.remove(this);
		});		
		var list = parentNode.children("ul:eq(0)");
		if(!list.length)
		{
			list = parentNode.append("<ul/>").children("ul:eq(0)");
			parentNode.removeClass("leaf").addClass("open");
		}
		list.append(elements);
		list.find("li:last-child").addClass("last").end().find("li:has(ul)").not(".open").addClass("closed");
		list.find("li").not(".open").not(".closed").addClass("leaf");
	}
	tree.creatingItem = false;
}

function createTreeItemDroppable(element, type, f)
{
	$(element).droppable({
		scope: "treeItem",
		accept: function(draggable)
		{
			var path = getTargetPath(getTreeNodePath(draggable.parent().get(0)));
			if(!path) path = draggable.attr("data-path");
			if(!path) return false;
			if(type && !instanceOf(getElementClass(path), type)) return false;
			return true;
		},
		drop: function(event, ui)
		{
			var path = getTargetPath(getTreeNodePath(ui.draggable.parent().get(0)));
			if(!path) path = ui.draggable.attr("data-path");
			f.call(event.target, path, event);
		}
	});
};

function createTreeItemDraggable(element)
{
	$(element).draggable({
		appendTo: "body",
		distance: 1,
		revert: "invalid",
		scope: "treeItem",
		cursorAt: { left: 5 },
		helper: function()
		{
			var path = $(this).attr("data-path");
			var name = getElementName(path);
			return $("<div/>").addClass("draghelper").css("background-image", getNodeIcon(getDataCollection(getElementPath(path)),name)).text(name);
		}
    });
}

function isTreeDummyExists(path)
{
	return getTreeNode("dummy!"+path) != undefined;
};

/**
 * Internal function for opening branch. Don't call it directly, use openBranch
 * instead
 * 
 * @see openBranch
 */
function doOpenBranch(treeObj, branch, pos, tries, size)
{
	if(tries <= 0) return;
	var departs = getPathComponents(branch);
	if(departs.length <= pos) return;
	var elementID = createPathFromComponents(departs.slice(0,pos+1));
	var element = document.getElementById("#rt_" + elementID);
	if (element) 
	{
		treeObj.select_branch(element);
		treeObj.open_branch(element);
		if(pos+1 == departs.length)
		{
			// finished
			element = $(element);
			var tree = element.closest(".tree_container");
			tree.scrollTop(Math.max(element.offset().top-tree.children().offset().top-tree.height()/2,0));
		} else
			doOpenBranch(treeObj, branch, pos + 1, 50, size==-1?-1:0);
	} else
	{
		var parentID = createPathFromComponents(departs.slice(0,pos));
		var parent = document.getElementById("#rt_"+parentID);
		var addMoreElements = document.getElementById("#et_"+parentID);
		if(size != -1 && addMoreElements && parent && treeObj.children(parent).length - 1 > size)
		{
			treeObj.select_branch(addMoreElements);
			setTimeout(function() {doOpenBranch(treeObj, branch, pos, 50, treeObj.children(parent).length - 1);}, 100);
		} else
			setTimeout(function() {doOpenBranch(treeObj, branch, pos, tries-1, size);}, 100);
	}
}

/**
 * Try to open given tree branch and select element (without clicking on it)
 * 
 * @param {String}
 *            branch - complete path to the branch
 * @param {Boolean}
 *            expand - whether try to expand long branches Note that it may give
 *            up in several cases: item not found; item is too far from
 *            collection start and expand set to false; loosy network connection
 *            and so on
 */
function openBranch(branch, expand)
{
	var departs = getPathComponents(branch);
	var treeObj = treeMap[departs[0]] || treeMap["databases"];
	for(var i=0; i<perspective.repository.length; i++)
	{
		if(perspective.repository[i].path == departs[0])
		{
			$('#repositoryTabs').tabs("select", i);
			break;
		}
	}
	doOpenBranch(treeObj, branch, 1, 50, expand?0:-1);
}

function markActiveTreeNode()
{
    $('#repositoryTabs').find(".active_node").removeClass("active_node");
    if (opennedDocuments[activeDocumentId] && opennedDocuments[activeDocumentId].completeName) 
    {
        var node = getTreeNode(opennedDocuments[activeDocumentId].completeName);
        if(node)
            $(node).addClass("active_node");
    }
}

function storeLastOpenPath(path)
{
    var key = currentUser + '_last_selected';
    document.cookie = [encodeURIComponent(key), '=', encodeURIComponent(path)].join('');
}

function openDefaultPath( callback )
{
    var key = currentUser + '_last_selected';
    var result = new RegExp('(?:^|; )' + encodeURIComponent(key) + '=([^;]*)').exec(document.cookie);
    if(result)
        callback (decodeURIComponent(result[1]));
    else
    {
        var projectPath = getDefaultResearchPath();
        if( projectPath )
            callback(projectPath);
        else
        {
            var defaultPath = appInfo.userProjectsPath;
            getDataCollection(defaultPath).getElementInfoAt(0, function(info)
        	{
        		var path = info ? createPath(defaultPath, info.name) + "/Data" : defaultPath;
                callback(path);
        	});
        }
    }
}

/*
 * Load name list callback class
 */
function LoadNameListCallback(treeObject, node, dc, itemsToLoad)
{
    this.treeObject = treeObject;
    this.node = node;
    this.dc = dc;
	var path = getTreeNodePath(node);
	if(!itemsToLoad) itemsToLoad = maxItemsToLoad; 
    this.func = function(size)
    {
        if (size > 100 && size > itemsToLoad) 
        {
            dc.getElementInfoRange(0, itemsToLoad, function(nameList)
            {
                if (nameList != null && nameList.length>0) 
                {
					createTreeElements(path, nameList);
                    $(treeObject.create(
                    {
                        attributes: 
                        {
                            id: "#et_" + path,
							totalSize: size
                        },
                        data: 
                        {
                            title: size-itemsToLoad<=itemsToLoad?resources.treeLoadLastItems.replace("{itemsToLoad}", size-itemsToLoad):
								resources.treeLoadNextItems.replace("{itemsToLoad}", itemsToLoad).replace("{itemsNotLoaded}", size-itemsToLoad)
                        }
                    }, node)).addClass("dummy");
                    dc.treeLoading = false;
                }
            });
        }
        else 
        {
            dc.getNameList(function(nameList)
            {
                if (nameList != null && nameList.length>0) 
                {
	                if (nameList != null) 
	                	createTreeElements(path, nameList);
	            } else
				{
					treeObject.creatingItem = true;
					treeObject.close_branch(node);
					treeObject.creatingItem = false;
				}
                dc.treeLoading = false;
            });
        }
    };
}

/*
 * Extend name list callback class
 */
function AddNameListCallback(treeObject, node, dc, from, itemsToLoad)
{
    this.treeObject = treeObject;
    this.node = node;
    this.dc = dc;
    this.from = from;
	if(!itemsToLoad) itemsToLoad = maxItemsToLoad; 
	var path = getTreeNodePath(node);
    this.func = function(nameList)
    {
		var totalSize = document.getElementById("#et_"+path).getAttribute("totalSize");
        if (nameList != null) 
        	createTreeElements(path, nameList);
		var curSize = treeObject.children(node).length;
		if (totalSize - curSize > 0) 
		{
            $(treeObject.create(
            {
                attributes: 
                {
                    id: "#et_" + path,
					totalSize: totalSize
                },
                data: 
                {
                    title: totalSize - curSize <= itemsToLoad ? 
                    		resources.treeLoadLastItems.replace("{itemsToLoad}", totalSize - curSize) :
                   			resources.treeLoadNextItems.replace("{itemsToLoad}", itemsToLoad).replace("{itemsNotLoaded}", totalSize - curSize)
                }
            }, node)).addClass("dummy");
		}
    };
}

/*
 * Protection status callback class
 */
function ProtectionStatusCallback(node)
{
    this.node = node;
    this.func = function(type, dc)
    {
        if (type == 0) 
        {
            node.children("a").get(0).style.backgroundImage = "url('icons/remoteNotProtectedDatabaseIcon.png')";
        }
        else 
            if (type == 1) 
            {
                node.children("a").get(0).style.backgroundImage = "url('icons/remotePublicReadDatabaseIcon.png')";
            }
            else if (type == 2 || type == 3 || type == 4) 
            { 
                var _type = type;
                var _dc = dc;
                dc.getPermission( function(perm, dc2)
                {
                    if (_type == 2) 
                    {
                        if(perm == 0)
                            node.children("a").get(0).style.backgroundImage = "url('icons/remotePublicDatabaseIcon2.png')";
                        else
                            node.children("a").get(0).style.backgroundImage = "url('icons/remotePublicDatabaseIcon.png')";
                    }
                    else 
                        if (_type == 3) 
                        {
                            if (perm == 0) 
                                node.children("a").get(0).style.backgroundImage = "url('icons/remoteProtectedReadDatabaseIcon2.png')";
                            else
                                node.children("a").get(0).style.backgroundImage = "url('icons/remoteProtectedReadDatabaseIcon.png')";
                        }
                        else 
                            if (_type == 4) 
                            {
                                if(perm == 0)
                                    node.children("a").get(0).style.backgroundImage = "url('icons/remoteProtectedDatabaseIcon2.png')";
                                else
                                    node.children("a").get(0).style.backgroundImage = "url('icons/remoteProtectedDatabaseIcon.png')";
                            }
                });
            }
    };
}

