var dragdrop = {};

//mouse position
dragdrop.mouseX = 0;
dragdrop.mouseY = 0;
dragdrop.offset_x = 0;
dragdrop.offset_y = 0;

//holder for element and its clone
dragdrop.element = null;
dragdrop.element_clone = null;
dragdrop.icon = null;
dragdrop.content = null;

dragdrop.element_width = null;
dragdrop.element_height = null;

//current target for drop
dragdrop.over_target = null;

//init element list - permit insertion after
dragdrop.after = [];
dragdrop.after.pos_x = [];
dragdrop.after.pos_y = [];
dragdrop.after.width = [];
dragdrop.after.height = [];
dragdrop.after.targets = [];

//init element list - permit insertion before
dragdrop.before = [];
dragdrop.before.pos_x = [];
dragdrop.before.pos_y = [];
dragdrop.before.width = [];
dragdrop.before.height = [];
dragdrop.before.targets = [];

//init element list - permit insertion inside
dragdrop.inside = [];
dragdrop.inside.pos_x = [];
dragdrop.inside.pos_y = [];
dragdrop.inside.width = [];
dragdrop.inside.height = [];
dragdrop.inside.targets = [];

dragdrop.init = function ()
{
	//register event handlers
	var document_element = document;
	if (document.body)
	{
		document_element = document.body;
	}
	else if (document.documentElement)
	{
		document_element = document.documentElement;
	}

	event_handler.add(document_element, 'mousemove', dragdrop.mouseMove);
	event_handler.add(document_element, 'mousedown', dragdrop.mouseDown);
	event_handler.add(document_element, 'mouseup', dragdrop.mouseUp);
};
dragdrop.mouseDown = function (event)
{
	event = event_handler.fix(event);
	var element = event.target;

	//if not dragable - terminate
	if (!class_handler.has(element, 'dragdrop_dragable'))
	{
		return;
	}

	//hold the element for reference
	dragdrop.element = element;
	if (event.preventDefault)
	{
		event.preventDefault();
	}

	//if it isn't freeroam - get possible targets, we'll need them
	if (!class_handler.has(element, 'dragdrop_freeroam'))
	{
		//init element list - permit insertion after
		dragdrop.after = [];
		dragdrop.after.pos_x = [];
		dragdrop.after.pos_y = [];
		dragdrop.after.width = [];
		dragdrop.after.height = [];
		dragdrop.after.targets = [];

		//init element list - permit insertion before
		dragdrop.before = [];
		dragdrop.before.pos_x = [];
		dragdrop.before.pos_y = [];
		dragdrop.before.width = [];
		dragdrop.before.height = [];
		dragdrop.before.targets = [];

		//init element list - permit insertion inside
		dragdrop.inside = [];
		dragdrop.inside.pos_x = [];
		dragdrop.inside.pos_y = [];
		dragdrop.inside.width = [];
		dragdrop.inside.height = [];
		dragdrop.inside.targets = [];

		//cycle through elements - get possible drop targets
		var elements = document.getElementsByTagName('*');
		for (var i = 0; i < elements.length; i++)
		{
			//decide drop target behaviour
			if (class_handler.has(elements[i], 'dragdrop_before'))
			{
				dragdrop.before.targets[dragdrop.before.targets.length] = elements[i];
			}

			if (class_handler.has(elements[i], 'dragdrop_after'))
			{
				dragdrop.after.targets[dragdrop.after.targets.length] = elements[i];
			}

			if (class_handler.has(elements[i], 'dragdrop_inside'))
			{
				dragdrop.inside.targets[dragdrop.inside.targets.length] = elements[i];
			}
		}

		//get position and dimensions of targets
		for (var i = 0; i < dragdrop.before.targets.length; i++)
		{
			dragdrop.before.pos_x[i] = position.getOffsetX(dragdrop.before.targets[i]);
			dragdrop.before.pos_y[i] = position.getOffsetY(dragdrop.before.targets[i]);
			dragdrop.before.width[i] = position.getOuterWidth(dragdrop.before.targets[i]);
			dragdrop.before.height[i] = position.getOuterHeight(dragdrop.before.targets[i]);
		}

		for (var i = 0; i < dragdrop.after.targets.length; i++)
		{
			dragdrop.after.pos_x[i] = position.getOffsetX(dragdrop.after.targets[i]);
			dragdrop.after.pos_y[i] = position.getOffsetY(dragdrop.after.targets[i]);
			dragdrop.after.width[i] = position.getOuterWidth(dragdrop.after.targets[i]);
			dragdrop.after.height[i] = position.getOuterHeight(dragdrop.after.targets[i]);
		}

		for (var i = 0; i < dragdrop.inside.targets.length; i++)
		{
			dragdrop.inside.pos_x[i] = position.getOffsetX(dragdrop.inside.targets[i]);
			dragdrop.inside.pos_y[i] = position.getOffsetY(dragdrop.inside.targets[i]);
			dragdrop.inside.width[i] = position.getOuterWidth(dragdrop.inside.targets[i]);
			dragdrop.inside.height[i] = position.getOuterHeight(dragdrop.inside.targets[i]);
		}
	}

	//clone the element
	dragdrop.element_clone = dragdrop.element.cloneNode(true);
	class_handler.add(dragdrop.element_clone, 'dragrdop_copy');

	if (document.body)
	{
		document.body.appendChild(dragdrop.element_clone);
	}
	else if (document.documentElement)
	{
		document.documentElement.appendChild(dragdrop.element_clone);
	}

	//get element position
	var elm_x = position.getOffsetX(dragdrop.element);
	var elm_y = position.getOffsetY(dragdrop.element);

	//get mouse position
	dragdrop.mouseX = position.getPositionX(event);
	dragdrop.mouseY = position.getPositionY(event);

	var tmp_img = dragdrop.element_clone.getElementsByTagName('img');
	for (var i = 0; i < tmp_img.length; i++)
	{
		if (class_handler.has(tmp_img[i], 'dragdrop_icon'))
		{
			dragdrop.icon = tmp_img[i];

			break;
		}
	}

	if (dragdrop.icon != null)
	{
		dragdrop.content = dragdrop.element_clone.innerHTML;

		dragdrop.element_clone.innerHTML = '';

		dragdrop.element_clone.appendChild(dragdrop.icon);

		dragdrop.icon.style.display = 'inline';

		dragdrop.element_clone.style.width = 'auto';
		dragdrop.element_clone.style.height = 'auto';
	}

	//set element dimensions
	dragdrop.width = position.getOuterWidth(dragdrop.element);
	dragdrop.height = position.getOuterHeight(dragdrop.element);

	//get mouse offset (click relative to upper left corner of element)
	dragdrop.offset_x = dragdrop.mouseX-elm_x;
	dragdrop.offset_y = dragdrop.mouseY-elm_y;

	//create a clone to move around
	var overlay = document.createElement('div');
	overlay.id = 'dragdrop_overlay';
	overlay.style.position = 'absolute';
	overlay.style.top = '0px';
	overlay.style.left = '0px';
	overlay.style.width = dragdrop.width+'px';
	overlay.style.height = (dragdrop.height)+'px';
	dragdrop.element_clone.appendChild(overlay);

	//position the clone over dragged element
	dragdrop.element_clone.style.position = 'absolute';
	dragdrop.element_clone.style.left = dragdrop.mouseX-dragdrop.offset_x+'px';
	dragdrop.element_clone.style.top = dragdrop.mouseY-dragdrop.offset_y+'px';
};
dragdrop.mouseMove = function (event)
{
	//if no element is dragged - terminate
	if (dragdrop.element == null)
	{
		return;
	}

	//prevent text selection
	if (event.preventDefault)
	{
		event.preventDefault();
	}

	//set mouse position
	dragdrop.mouseX = position.getPositionX(event);
	dragdrop.mouseY = position.getPositionY(event);

	//set clone position
	dragdrop.element_clone.style.left = dragdrop.mouseX-dragdrop.offset_x+'px';
	dragdrop.element_clone.style.top = dragdrop.mouseY-dragdrop.offset_y+'px';

	//if it is freeroam - just leave it at cursor
	if (class_handler.has(dragdrop.element_clone, 'dragdrop_freeroam'))
	{
		return;
	}

	//reset default value
	dragdrop.over_target = false;

	//targets for insert inside
	for (var i = 0; i < dragdrop.inside.targets.length; i++)
	{
		//exclude dragged element
		if (dragdrop.inside.targets[i] === dragdrop.element)
		{
			continue;
		}

		//if dragged element is over target
		if (dragdrop.mouseX > dragdrop.inside.pos_x[i]
		&& dragdrop.mouseX < dragdrop.inside.pos_x[i]+dragdrop.inside.width[i]
		&& dragdrop.mouseY > dragdrop.inside.pos_y[i]
		&& dragdrop.mouseY < dragdrop.inside.pos_y[i]+dragdrop.inside.height[i])
		{
			var tmp_target = dragdrop.inside.targets[i];
			var target_pos_y = dragdrop.inside.pos_y[i];
			var target_height = dragdrop.inside.height[i];

			dragdrop.over_target = true;
		}
	}

	//targets for insert before
	for (var i = 0; i < dragdrop.before.targets.length; i++)
	{
		//exclude dragged element
		if (dragdrop.before.targets[i] === dragdrop.element)
		{
			continue;
		}

		//if dragged element is over target
		if (dragdrop.mouseX > dragdrop.before.pos_x[i]
		&& dragdrop.mouseX < dragdrop.before.pos_x[i]+dragdrop.before.width[i]
		&& dragdrop.mouseY > dragdrop.before.pos_y[i]
		&& dragdrop.mouseY < dragdrop.before.pos_y[i]+dragdrop.before.height[i])
		{
			var tmp_target = dragdrop.before.targets[i];
			var target_pos_y = dragdrop.before.pos_y[i];
			var target_height = dragdrop.before.height[i];

			dragdrop.over_target = true;
		}
	}

	//targets for insert after
	for (var i = 0; i < dragdrop.after.targets.length; i++)
	{
		//exclude dragged element
		if (dragdrop.after.targets[i] === dragdrop.element)
		{
			continue;
		}

		//if dragged element is over target
		if (dragdrop.mouseX > dragdrop.after.pos_x[i]
		&& dragdrop.mouseX < dragdrop.after.pos_x[i]+dragdrop.after.width[i]
		&& dragdrop.mouseY > dragdrop.after.pos_y[i]
		&& dragdrop.mouseY < dragdrop.after.pos_y[i]+dragdrop.after.height[i])
		{
			var tmp_target = dragdrop.after.targets[i];
			var target_pos_y = dragdrop.after.pos_y[i];
			var target_height = dragdrop.after.height[i];

			dragdrop.over_target = true;
		}
	}

	//if no target is set - terminate
	if (!tmp_target)
	{
		return;
	}

	//if target is fixed - terminate
	if (class_handler.has(tmp_target, 'dragdrop_stay'))
	{
		return;
	}

	if (class_handler.has(tmp_target, 'dragdrop_inside'))
	{
		//if item can be dropped inside target
		tmp_target.appendChild(dragdrop.element_clone);
	}
	else if (class_handler.has(tmp_target, 'dragdrop_before') && class_handler.has(tmp_target, 'dragdrop_after'))
	{
		//if item can be dropped before or after target
		if (dragdrop.mouseY > target_pos_y+target_height/2)
		{
			dom_handler.insertAfter(dragdrop.element_clone, tmp_target);
		}
		else
		{
			tmp_target.parentNode.insertBefore(dragdrop.element_clone, tmp_target);
		}
	}
	else if (class_handler.has(tmp_target, 'dragdrop_before'))
	{
		//if item can be dropped before target
		tmp_target.parentNode.insertBefore(dragdrop.element_clone, tmp_target);
	}
	else if (class_handler.has(tmp_target, 'dragdrop_after'))
	{
		//if item can be dropped after target
		dom_handler.insertAfter(dragdrop.element_clone, tmp_target);
	}

	//set clone to relative position
	dragdrop.element_clone.style.position = 'relative';
	dragdrop.element_clone.style.left = '';
	dragdrop.element_clone.style.top = '';
};
dragdrop.mouseUp = function (event)
{
	//if no element is dragged - terminate
	if (dragdrop.element == null)
	{
		return;
	}

	//prevent text selection
	if (event.preventDefault)
	{
		event.preventDefault();
	}

	//remove overlay from dragged element
	var overlay = document.getElementById('dragdrop_overlay');
	if (overlay)
	{
		overlay.parentNode.removeChild(overlay);
	}

	//clones should not stay - must be movable and removable
	if (class_handler.has(dragdrop.element, 'dragdrop_stay'))
	{
		class_handler.remove(dragdrop.element_clone, 'dragdrop_stay');
	}

	//if not freeroam - reset position
	if (!class_handler.has(dragdrop.element_clone, 'dragdrop_freeroam'))
	{
		//set position back to relative
		dragdrop.element_clone.style.position = 'relative';
		dragdrop.element_clone.style.left = '';
		dragdrop.element_clone.style.top = '';
	}
	else
	{
		if (!class_handler.has(dragdrop.element, 'dragdrop_stay'))
		{
			dragdrop.element.parentNode.removeChild(dragdrop.element);
		}

		class_handler.remove(dragdrop.element_clone, 'dragrdop_copy');
	}

	//if there is no target
	if (!dragdrop.over_target)
	{
		//if not freeroam - destroy clone if it's not over drop target
		if (!class_handler.has(dragdrop.element_clone, 'dragdrop_freeroam'))
		{
			//destroy clone
			dragdrop.element_clone.parentNode.removeChild(dragdrop.element_clone);
		}

		//if it is a copy we dragged - destroy the element too
		if (class_handler.has(dragdrop.element, 'dragrdop_copy'))
		{
			dragdrop.element.parentNode.removeChild(dragdrop.element);
		}
	}
	else
	{
		//if it is freeroam - we don't care about targets
		if (!class_handler.has(dragdrop.element, 'dragdrop_freeroam'))
		{
			//if the element is not fixed - destroy it
			if (!class_handler.has(dragdrop.element, 'dragdrop_stay'))
			{
				dragdrop.element.parentNode.removeChild(dragdrop.element);
			}
		}
	}

	if (dragdrop.icon != null)
	{
		dragdrop.element_clone.innerHTML = dragdrop.content;

		dragdrop.content = null;

		dragdrop.icon.style.display = 'none';

		dragdrop.icon = null;

		if (dragdrop.element.clientWidth)
		{
			dragdrop.width = dragdrop.element.clientWidth;
			dragdrop.height = dragdrop.element.clientHeight;
		}

		dragdrop.element_clone.style.width = dragdrop.width+'px';
		dragdrop.element_clone.style.height = dragdrop.height+'px';
	}

	//reset values
	dragdrop.offset_x = 0;
	dragdrop.offset_y = 0;
	dragdrop.element = null;
	dragdrop.element_clone = null;
};
