// operation type switch (switches operation between 'move' and 'copy')
	function getOpTypeSwitch()
	{
		return $('input[name=MoveCopy]')
	}
	
	function setOpSwitchEvents()
	{
		var opSwitcher = getOpTypeSwitch();
		opSwitcher
			.click(function()
					{
						var op = getOperationType();
						updateDraggableItems(op);
					});	
	}
	
	function getOperationType()
	{
		var opSwitcher = getOpTypeSwitch();
		return opSwitcher.filter(':checked').val()
	}

// draggable items, the items being dragged from one group to another
	function getDraggableItems()
	{
		return $("ul#Groups ul>li").add("ul#UncategorizedGroupItems ul>li")
	}
	
	function initDraggableItems(draggableItems)
	{
		if(!draggableItems)
			var draggableItems = getDraggableItems();
			
		var op = getOperationType();
		draggableItems
			.each(function()
				  {
					 var di = $(this);
					 var diData = getNodeData(di);
					 
					 var dig = di.parents('li:first');
					 if(dig.size() > 0)
					 {
						 var digData = getNodeData(dig);
						 diData.groupId = digData.groupId; // reads the groupId from the parent node, and saves it
					 }
					 else
					 {
						 diData.groupId = 0; // groupId is 0 for uncategorized items
					 }
					 saveNodeData(di, diData); // save groupId as item data as well
				  })
			.draggable({
						helper: getDragHelperByOpType(op),
						revert: 'invalid',
						start: function(event, ui)
								{
									ui.helper.removeClass('saveFailed');
								}
					})
	}
	
	// updated draggable items helper when the operation type is changed
	function updateDraggableItems(op)
	{
		var draggableItems = getDraggableItems();
		draggableItems
			.draggable('option', 'helper', getDragHelperByOpType(op));
	}
	
	// checks if an item is in a specific group, by itemId
	function itemInGroup(newGroupItemData, group)
	{
		var result = false;
		var groupItems = group.find('li');
		groupItems
			.each(function()
					{
						var gi = jQuery(this);
						var giData = getNodeData(gi);
						if(giData.itemId == newGroupItemData.itemId)
						{
							result = true;
							return
						}
					})
		return result;
	}
	
	// resets style of the draggable item after being dropped(removes left and right positioning)
	function resetDraggableItemStyle(draggableItem)
	{
		draggableItem
			.attr('style', 'position:relative')
	}
	
	// returns the drag helper type, by operation. if operation is 'copy', the draggable helper will be a clone of the element, the element itself for 'move'
	function getDragHelperByOpType(op, groupId)
	{
		return (op == 'copy')?'clone':'original'
	}

// droppable items, the items to which draggable items are dropped
	function getDroppableItems()
	{
		return $("ul#Groups:first>li")
	}

	function initDroppableItems()
	{
		var droppableItems = getDroppableItems();
		droppableItems
			.each(function()
					{
						var droppableItem = $(this);
						if(droppableItem.find('ul:first').size() == 0)
							droppableItem.append('<ul></ul>');
					})
			.droppable({
						drop: function(event, ui)
								{
									var destGroup = $(this);
									
									var groupItemData = getNodeData(ui.draggable);
									var destGroupData = getNodeData(destGroup);
									
									resetDraggableItemStyle(ui.draggable);
									
									if(itemInGroup(groupItemData, destGroup))
										return false;
									
									
									groupItemData.destGroupId = destGroupData.groupId;
									saveNodeData(ui.draggable, groupItemData);
									
									var op = getOperationType();
									
									 // override operation type to always be 'move' when the item is dragged from the uncategorized list
									if(groupItemData.groupId == 0) op = 'move';
									if (op == 'copy')
									{
										var ItemClone = ui.draggable.clone();
										ItemClone.appendTo(destGroup.find('ul:first'));
										initDraggableItems(ItemClone);
										saveChangeToServer(ItemClone, op);
									}
									else
									{
										ui.draggable.appendTo(destGroup.find('ul:first'));
										saveChangeToServer(ui.draggable, op);
									}
								},
						hoverClass: 'drophover'
					})
	}
	
/* Sends and AJAX request to the server script, containing the following params: 
 * @itemId - the id of the item being moved/copy
 * @op - the operation made, one of 'move','copy'
 * @srcGroupId - the id of the current group the item is belonging to
 * @destGroupId - the id of the detination group to which the item is moved/copied to
 * @srcGroupId and @destGroupId are 0(zero) for the 'Uncategorized Items' group
 */
	function saveChangeToServer(groupItem, op)
	{
		groupItem.draggable('disable').addClass('saving');
		var itemData = getNodeData(groupItem);
		var itemSaved = groupItem;
		$.ajax({
					url: getCICacheUrl(getGroupOpBaseUrl() + 'change_favorites.html'),
					data: 'op=' + op + 
							'&itemId=' + itemData.itemId + 
							'&srcGroupId=' + itemData.groupId + 
							'&destGroupId=' + itemData.destGroupId,
					type: 'POST',
					success: function(JSONresp)
								{
									var serverData = eval('(' + JSONresp + ')');
									if (serverData.Error == 0) // Error == 0, successfull save
									{
										itemSaved
											.removeClass('saving')
											.draggable('enable');
											
										if(itemData.groupId == 0)
										itemSaved
											.append(
														'<a class="lnkDeleteItem" href="' + getGroupOpBaseUrl() + 'remove-favorites-item/' + itemData.destGroupId + '/' + itemData.itemId + '.html" title="Remove item from group" onclick="return confirm(\'Remove item from group?\');">-</a>'
													);
											
										var newItemData = {
																itemId: itemData.itemId,
																groupId: itemData.destGroupId
															};
										saveNodeData(itemSaved, newItemData); // update node data with data from server
									}
									else
									{
										if (op == 'copy')// copy failed
										{
											// copy failed, remove clone
											var draggableItems = getDraggableItems();
											//draggableItems.filter('[title=' + getNodeSerializedData(itemSaved) + ']').addClass('saveFailed');
											itemSaved.remove();
										}
										else // move failed
										{
											// move failed, update visual flag, move it back, make it draggable
											var droppableItems = getDroppableItems();
											itemSaved
												.removeClass('saving')
												.addClass('saveFailed')
												.appendTo(droppableItems.filter('[title={groupId:' + itemData.groupId + '}]'))
												.draggable('enable');
										}
									}
										
									// check if operation is performed on the current group, and refresh the page if so
									var currentGroupId = getCurrentGroupId();
									if((currentGroupId == itemData.destGroupId) || (currentGroupId == itemData.groupId))
										location.reload();
								},
					error: function(err)
							{
										if (op == 'copy')
										{
											// copy failed, remove clone
											var draggableItems = getDraggableItems();
											//draggableItems.filter('[title=' + getNodeSerializedData(itemSaved) + ']').addClass('saveFailed');
											itemSaved.remove();
										}
										else
										{
											// move failed, update visual flag, move it back, make it draggable
											var droppableItems = getDroppableItems();
											itemSaved
												.removeClass('saving')
												.addClass('saveFailed')
												.appendTo(droppableItems.filter('[title={groupId:' + itemData.groupId + '}]'))
												.draggable('enable');
										}
										
										// check if operation is performed on the current group, and refresh the page if so
										var currentGroupId = getCurrentGroupId();
										if((currentGroupId == itemData.destGroupId) || (currentGroupId == itemData.groupId))
											location.reload();
							}
			   })
	}
	
	function getGroupOpBaseUrl()
	{
		return getNodeData($('#Groups')).base_url;
	}
	
	function getCurrentGroupId()
	{
		var groupNodeData = getNodeData($('#Groups'));
		if (groupNodeData.current_group_id)
			return parseInt((groupNodeData.current_group_id))
		return null;
	}

// common functions 
	function getNodeData(node, dataAttr)
	{
		var serializedData = getNodeSerializedData(node, dataAttr);
		return eval('(' + serializedData + ')');
	}
	
	function getNodeSerializedData(node, dataAttr)
	{
		if(!dataAttr) dataAttr = 'title';
		return node.attr(dataAttr);
	}
	
	function saveNodeData(node, dataObj, dataAttr)
	{
		if(!dataAttr) dataAttr = 'title';
		node.attr(dataAttr, serializeSimpleObject(dataObj));
	}
				 
	function serializeSimpleObject(o)
	{
		var res = new String('{');
		for (var i in o)
			res += i + ':' + o[i] + ', '
		return res.substr(0, res.length-2) + '}';
	}

function initGroupSorting()
{
	if($('#Groups').size()>0)
	{
		setOpSwitchEvents();
		initDraggableItems();
		initDroppableItems();
	}
}