/*global window, $, jQuery*/

if (typeof String.prototype.trim !== 'function') {
	String.prototype.trim = function () {
		"use strict";
		return this.replace(/^\s+|\s+$/g, '');
	};
}

(function ($, undef) {
	"use strict";

	$.treeDragDrop = {
		
		defaults: {			
			selectedClass: "tdd-selected",
			collapsedClass: "tdd-collapsed",
			expandedClass: "tdd-expanded",
			beforeClass: "tdd-before", 
			afterClass: "tdd-after",
			cursorGrabbingUrl: null,
			inFolderThreshhold: 100,
			cursorAt: {left: 10, top: -40}, 
			dragContainer: $('<div class="tdd-dragContainer" />'),			
			marker: $('<div />'),
			attributes: ["id", "class"],
			getUrl: null,
			updateUrl: null		
		}		
	};	
	
	
	// helpers
	
	function debug() {
		if (window.console && window.console.log) {
			//window.console.log(arguments);
		}
	}
			
	function getContext(el) {
		return el.closest(".treeDragDrop");
	}
	
	function getOptions(el) {
		return el.closest(".treeDragDrop").data("options");
	}
	
	function serializeTree(el, ctx) {
		var data = [],
			intestingAttr = getOptions(el).attributes;
		
		el.children("li").each(function (index, value) {			
			var obj = {},
				attr = {};
				
			obj.data = $(value).clone().children().remove().end().text().trim();
			$.each(intestingAttr, function (index, attribute) {
				if ($(value).attr(attribute) !== undef) {
					if (attribute === "class") {
						attr.classname = $(value).attr(attribute);
					} else {
						attr[attribute] = $(value).attr(attribute);
					}
				}
			});			
			obj.attr = attr;								
			if ($(value).children("ul").length > 0) {
				obj.children = serializeTree($(value).children("ul"));
			}			
			data.push(obj);
		});	
		
		return data;
	}
		
	function sendTree(treeData, updateUrl) {
		var ajaxData;
			//treeData = serializeTree(tree);

		debug(treeData);		
		if (updateUrl !== null) {
			ajaxData = {tree: treeData};
			$.post(updateUrl, ajaxData, function (res) {
				//TODO: error handling
				return true;
			}).done(function (msg) {
				showInfoMessage('Uloženo','success');
			}).fail(function(xhr, status, error) {
				showInfoMessage('Chyba: ' + xhr.responseText,'danger');
			});
		}	
	}
		
	// handlers	
		
	$.treeDragDrop.handlers = {
				
		handleDraggableStart: function (e, o) 
		{			
			debug("handleDraggableStart");	
			var options = getOptions($(e.target));			
			$(e.target).addClass(options.selectedClass);
			document.onmousemove = function () {
				return false;
			};	
			
			$(e.target).data('position', $(e.target).index());
			
			options.dropped = false;
			
			//$("body").css("cursor", "url(" + options.cursorGrabbingUrl + ") , move").addClass("cursorGrabbing");
		},
		
		handleDraggableDrag: function (e, o) {
			debug("handleDraggableDrag");	
		},
		
		handleDraggableStop: function (e, o) {
			debug("handleDraggableStop");
			
			var ctx = getContext($(e.target)),
				options = getOptions($(e.target)),			
				tree = $(".tdd-tree", ctx),
				target = $(e.target);			
			// remove the mousemove Listener			
			$("li, .tdd-tree", ctx).unbind("mousemove").removeClass(options.selectedClass);
			
			var data = {
			      id: target.data('id'), 
			      target: target.closest("ul").closest("li").data("id"),
			      position: target.index(),
               after: target.prev().data('id'),
               before: target.next().data('id'),
	      };
			
         if ($(e.target).data('position') <= data.position)
            data.position++;
			
			// build the array and post the ajax
			debug("handleDraggableStop: sendTree");
			sendTree(data, options.updateUrl);
			
			//$("body").removeClass("cursorGrabbing").css("cursor", "auto");
		},
		
		handleDroppableOut: function (e, o) {
		   debug("handleDroppableOut", e.target);
			$(e.target).unbind("mousemove");						
		},
		
		handleDroppableOver: function (e, o) {
			debug("handleDroppableOver", e.target);
			var	options = getOptions($(e.target)),
				selectedClass = options.selectedClass,
				beforeClass = options.beforeClass,
				afterClass = options.afterClass,
				marker = options.marker;		
			
			if ($(e.target).is("li")) {
				// bind MouseMove to the item to check if the draggable should be appended or placed before or after the item 
				$(e.target).bind("mousemove", function (mme) {
				   //debug("handleDroppableOver mousemove", mme.currentTarget);
					var target = $(mme.target).closest("li, ul"),						
						x = mme.pageX - target.offset().left,
						y = mme.pageY - target.offset().top,
						threshhold = options.inFolderThreshhold;
					
					// threshhold for apending or placing before/ater
					// will grow according to the deepness of nesting
					
					if (target.find("ul").length !== 0) {
						threshhold = Math.min(options.inFolderThreshhold * (target.find("ul").length + 1), target.width() * 0.75);
					}
					
					marker.removeClass(beforeClass, afterClass);
										
					// prevent dropping items in itself
					if (target.hasClass(selectedClass) || target.parents("." + selectedClass).length !== 0) {
						marker.detach();
					} else if (target.parents(".tdd-trashbin").length !== 0) {						
						target.parents(".tdd-trashbin").append(marker);						
					} else {
						// append to item
						if (x > threshhold) {							
							// append to ul if there is one
							if (target.is("li") && target.children("ul").length !== 0) {
								target.children("ul").append(marker);
							} else if (target.is("li")) {
								target.append(marker);
							}
						// place before item	
						} else if (y < target.height() / 2) {
							marker.addClass(beforeClass);
							target.before(marker);
						// place after item
						} else {
							marker.addClass(afterClass);
							target.after(marker);
						}
					}
							
					return false;
				});
							
			// if tree is empty items may be put in the ul 
			//} else if ($(e.target).hasClass("tdd-tree")/* && $(".tdd-tree").children().length === 0 */) {
			} else if ($(e.target).hasClass("tdd-tree")) {
				debug("tree");
				marker.removeClass(beforeClass, afterClass);
				marker.addClass(beforeClass);
				$(e.target).append(marker);
			} else if ($(e.target).hasClass("tdd-trashbin")) {
				debug("trashbin");
				$(e.target).append(marker);
			}
			
			return false;	
		},
		
		handleDroppableDrop: function (e, o) 
		{
			debug("handleDroppableDrop");
			
			var	draggable = $(o.draggable),
				dropable = $(e.target),
				marker = $.treeDragDrop.defaults.marker,
				ctx = draggable.data("tddCtx"),
				options = ctx.data("options");
				
			// remove selection	
			draggable.removeClass(options.selectedClass);
			
			if (options.dropped)
			   return true;
			
			options.dropped = true;
			
			// if its the trashbin put them all next to each other (no nesting)
			if (dropable.parents(".tdd-trashbin").length !== 0 || dropable.hasClass("tdd-trashbin")) {
				$(".tdd-trashbin").append(draggable);	
				$("li", draggable).each(function (index, value) {
					$(".tdd-trashbin").append(value);
				});				
				
			// put the item directly in the tree ul if it contains no other element	
			} else if (dropable.hasClass("tdd-tree") && $(".tdd-tree").children().length === 0) {
				$(".tdd-tree").append(draggable);
			// otherwise put it before the marker, which will be detached asap
			} else {				
				marker.before(draggable);				
				if (draggable.parent().is("li")) {					
					draggable.wrap("<ul></ul>");				
				}
			}			
			marker.detach();
			//clean up empty uls if its not the tree or trashbin
			$("ul", ctx).not(".tdd-trashbin, .tdd-tree").each(function () {
				if ($(this).children().length === 0) {
					debug($(this));
					$(this).remove();					
				}
			});	
			// adjust expand/collapse icons 
			$("li", ctx).has("ul").not("li." + options.collapsedClass).addClass(options.expandedClass);
			$("li", ctx).not("li:has(ul)").removeClass(options.expandedClass).removeClass(options.collapsedClass);
			
			return false;
		},
		
		
		// toggle expand/collapse
		handleClick: function (e) {
			
			var target = $(e.target),
				ctx = getContext($(e.target)),
				tree = $(".tdd-tree", ctx),
				options = getOptions($(e.target)),
				collapsed = options.collapsedClass,
				expanded = options.expandedClass;
			
			
			if (target.children("ul").length === 0) {				
				return false;				
			} else {			
				if (target.hasClass(collapsed)) {
					target.removeClass(collapsed).addClass(expanded);					
					target.children("ul").show();					
				} else {
					target.removeClass(expanded).addClass(collapsed);	
					target.children("ul").hide();
				}
			}	
			
			debug("handleClick: sendTree");
			sendTree(tree, options.updateUrl);	
					
			e.stopImmediatePropagation();
		}
	};
	
	
	// the Prototype
		
	$.fn.treeDragDrop = function (options) {
		
		//extend the global default with the options for the element
		options = $.extend({}, $.treeDragDrop.defaults, options);
		
		
		
		return this.each(function () {
			var ctx = $(this),
				data = ctx.data('treeDragDrop');
			
			// init the element
			if (!data) {	
				$("li", ctx).draggable({ 
					addClasses: false,
					cursorAt:  $.treeDragDrop.defaults.cursorAt,
					helper: "clone",
					appendTo: "body",
					opacity: 0.2,
					delay: 10,
					handle: ".drag-drop-mover", 
					//create: $.treeDragDrop.handlers.handleDraggableCreate,
					start: $.treeDragDrop.handlers.handleDraggableStart,
					//drag: $.treeDragDrop.handlers.handleDraggableDrag,
					stop: $.treeDragDrop.handlers.handleDraggableStop
				});
				$("li, ul", ctx).droppable({
					addClasses: false,
					greedy: true,
					tolerance: "pointer",
					drop: $.treeDragDrop.handlers.handleDroppableDrop,
					over: $.treeDragDrop.handlers.handleDroppableOver,
					out: $.treeDragDrop.handlers.handleDroppableOut
					
				}).data("tddCtx", ctx);

				$.treeDragDrop.defaults.marker.bind("mousemove", function () { return false; });
				$.treeDragDrop.defaults.marker.bind("mouseover", function () { return false; });
			
				ctx.data('options',  options);											
				ctx.data('treeDragDrop', {inited: true});				
			}				
		});
	};
	
}(jQuery));

