var containers         = [];
var state_mousedown    = false;
var dragging_component = null; // Current component being dragged
var drag_offset_x      = 2;
var drag_offset_y      = 10;
var helper_component   = null;
var last_target        = null;

/***** Setup onload function *****/
if (typeof window.addEventListener != 'undefined')
{
	window.addEventListener('load', dd_init, false); // gecko, safari, konqueror and standard.
}
else if (typeof document.addEventListener != 'undefined')
{
	document.addEventListener('load', dd_init, false); // opera 7
}
else if (typeof window.attachEvent != 'undefined')
{
	window.attachEvent('onload', dd_init); // IE
}

/**
 * Helper function to apply special styles/properties to a component when it is being dragged
 */
function dd_beginComponentDrag(component)
{
	if (!component || !component.className)
	{
		return;
	}

	component.className += ' dropzone';
}

/**
 * Helper function to apply special styles/properties to a component when it is dropped
 */
function dd_endComponentDrag(component)
{
	if (!component || !component.className)
	{
		return;
	}

	component.className = component.className.replace(/ dropzone/, '');
}

/**
 * Create helper object, setup events and assign drag&drop containers
 */
function dd_init()
{
	helper_component = document.createElement('div');
	helper_component.style.display = 'none'; 
	helper_component.style.opacity = '0.5'; 
	helper_component.style.filter  = 'alpha(opacity=50)'; 

	document.body.appendChild(helper_component);

	// Create the drag containers
	dd_setDragContainers('drag_container_1', 'drag_container_2', 'drag_container_3');

	$(document).bind('mousemove', dd_event_mouseMove );
	$(document).bind('mouseup',   dd_event_mouseUp );


}

/**
 * Set up the drag containers
 * Loop through the containers passed, loop through their children, looking for an element that has a child in it with 
 * an id that contains "[_]header[_]" to determine which components are draggable
 */
function dd_setDragContainers()
{
	for (var i = 0; i < arguments.length; i++)
	{
		var container_object = document.getElementById(arguments[i]);
		if (container_object)
		{
			containers.push([i + 1, arguments[i]]);
			var draggable_components = dd_getDraggableComponents(container_object);
			for (var j = 0; j < draggable_components.length; j++)
			{
				dd_makeDraggable(i,arguments[i],draggable_components[j]);
			}
		}
	}
}

/**
 * Call a webservice on the server
 * @param container_id {String} The col number
 * @param container {String} The id of the dd col
 * @param component {Object} The component dom element to make draggable
 */
function dd_makeDraggable(container_id,container,component)
{
				component.onmousemove = dd_event_mouseMove;
				component.setAttribute('drag_container', container);
				component.setAttribute('drag_container_id', container_id);
				component.drag_handle = dd_findDragHandle(component);
				component.drag_handle.setAttribute('drag_object', component);

				Utils.disableSelection(component.drag_handle);
				$(component).find('a.comp_header_close').click(function(){return false;}).bind( 'mousedown', 
					function ()
					{
						$(component).fadeOut('fast', 
							function()
							{ 
								$(this).remove(); 
								dd_writeCookie(); 
							}
						);
						return false; 
					} 
				);

				$(component.drag_handle).bind('mousedown', dd_event_mouseDown );
}

/**
 * Function callback for mouse move event
 */
function dd_event_mouseMove(ev)
{
	ev = ev || window.event;

	if (!window.state_mousedown) 
	{
		return true;
	}

	//Set the target to whatever item the mouse is current on.
	var pos    = Utils.mouseCoords(ev);

	// Make sure drag container is good
	var drag_container = document.getElementById(dragging_component.getAttribute('drag_container'));
	if (!drag_container) 
	{
		return false;
	}

	// Move the helper component
	helper_component.style.top  = (pos.y - drag_offset_y) + 'px';

	var sibling = dd_getNextSibling(drag_container, dragging_component, pos.x, pos.y);
	if (sibling == null)
	{ // Append to end of container
		if (dragging_component.nextSibling) // Only if component not already last child
		{
			drag_container.appendChild(dragging_component);
		}
	}
	else
	{ // Insert before existing
		if (dragging_component.nextSibling != sibling)
		{
			drag_container.insertBefore(dragging_component, sibling);
		}
	}

	return false;
}

/**
 * Function callback for mouse down event 
 */
function dd_event_mouseUp(ev)
{
	ev         = ev || window.event;
	var target = ev.target || ev.srcElement;
	
	if (!window.state_mousedown) 
	{
		return true;
	}
	window.state_mousedown = false;

	if (dragging_component)
	{
		helper_component.style.display = 'none';
		dd_endComponentDrag(dragging_component);
		dragging_component = null;
	}
	else
	{
		return true;
	}

	// Write drag&drop cookie
	dd_writeCookie();
}

/**
 * Function to write the dd cookie 
 */
function dd_writeCookie()
{
	var page = window.page_index ? window.page_index : 1;
	var cookie = [];
	for (i = 0; i < containers.length; i++)
	{
		var drag_container_id = containers[i][0];
		var drag_container    = document.getElementById(containers[i][1]);
		if (drag_container && drag_container_id)
		{

			var components = dd_getDraggableComponents(drag_container);
			for (j = 0; j < components.length; j++)
			{
				var matches = components[j].id.match(/syn_comp_([\w]+)/);
				var comps = [];
				if (matches && matches[1])
				{
					comps.push( drag_container_id + '[]=' + matches[1] );
				}
				cookie.push( comps.join('&') );
			}
			
			if (!components.length)
			{
				cookie.push( drag_container_id+'[]=null' );
			}

		}
	}

	document.cookie = 'chekdragndrop_' + page + '=' + cookie.join('&');
}

/**
 * Function callback for mouse down event
 */
function dd_event_mouseDown(ev)
{
	// If mouse state is down when this is called, the user clicked the mouse down and dragged 
	// off the viewport, then released the mouse.  We need to make sure to trigger the mouse up
	// event before doing anything further so nothing is lost
	if (state_mousedown == true)
	{
		dd_event_mouseUp(ev);
	}
	
	ev         = ev || window.event;
	var target = ev.target || ev.srcElement;
	var element = target;

	while (element)
	{
		if (element.getAttribute && element.getAttribute('drag_object'))
		{
			dragging_component = element.parentNode;

			var drag_component_position = Utils.getPosition(dragging_component);
			var mousePos                = Utils.mouseCoords(ev);

			// Clone dragging component and place inside helper div
			for (i = 0; i < helper_component.childNodes.length; i++) 
				helper_component.removeChild(helper_component.childNodes[i]);

			helper_component.appendChild(dragging_component.cloneNode(true));
			helper_component.firstChild.removeAttribute('drag_container');

			var drag_container = document.getElementById(dragging_component.getAttribute('drag_container'));
			
			if (drag_container)
			{
				if (drag_container.childNodes.length <= 1)
				{
					return true;
				}

				var dc_pos = $(drag_container).offset();

				helper_component.style.width   = drag_container.offsetWidth + 'px';
				helper_component.style.height  = (dragging_component.offsetHeight + 5) + 'px';

				// Hide the component that will be dragged, but keep it's region in place
				dd_beginComponentDrag(dragging_component);

				// Figure out offset from where clicked on drag div to the topmost point of drag div
				drag_offset_y = mousePos.y - Utils.getPosition(element).y;

				// Force helper inside container to preserve styles
				drag_container.appendChild(helper_component);

				// Move helper component and show
				helper_component.style.position = 'absolute';
				helper_component.style.left = (dc_pos.left + drag_offset_x) + 'px'; 
				helper_component.style.top  = (mousePos.y - drag_offset_y) + 'px';
				helper_component.style.display = 'block';

				window.state_mousedown = true;
				return false;
			}
		}
		element = element.parentNode;
	}

	return true;
}

/**
 * Does hit-tests to determine which component another component should be placed before
 * Returns null if component should be placed at end of container
 */
function dd_getNextSibling(container, component, x, y)
{
	if (!container || !component)
	{
		return;
	}

	var components = dd_getDraggableComponents(container);

	// If y pos of mouse before container's top pos, place at beginning
	var container_pos = Utils.getPosition(container);
	if (y <= container_pos.y)
	{
		return components[0];
	}

	for (i = 0; i < components.length; i++)
	{
		var pos = Utils.getPosition(components[i]);

		var pos = Utils.getPosition(components[i]);
		if (Utils.horizontalHitTest(components[i], y))
		{
			var midpoint = (components[i].offsetHeight / 2) + pos.y;

			if (y <= midpoint)
			{ // 1st half of hover component
				return components[i];
			}
			else
			{
				if (components[i].nextSibling)
				{
					return components[i].nextSibling;
				}
				else
				{
					return null;
				}
			}
		}
		else if (y < pos.y)
		{
				return components[i];
		}
	}

	return null;
}

/**
 * Determines if element is contained within a draggable component
 * Returns the component object if so, otherwise return null
 */
function dd_elementWithinDraggableComponent(element)
{
	while (element)
	{
		if (element.getAttribute && element.getAttribute('drag_container'))
		{
			return element;
		}
		element = element.parentNode;
	}
	return null;
}

/**
 * Return an array of components that are draggable in passed container
 */
function dd_getDraggableComponents(container_object)
{
	if (!container_object) 
	{
		return null;
	}

	var components = [];
	for (var i = 0; i < container_object.childNodes.length; i++)
	{
		if (container_object.childNodes[i].nodeName == '#text') 
		{
			continue;
		}

		var drag_handle = dd_findDragHandle(container_object.childNodes[i]);
		if (drag_handle != null)
		{
			container_object.childNodes[i].drag_handle = drag_handle;
			components.push(container_object.childNodes[i]);
		}
	}

	return components;
}

/**
 * Finds the drag handle element within a draggable component
 */
function dd_findDragHandle(component)
{
	if (!component)
	{
		return null;
	}

	for (var i = 0; i < component.childNodes.length; i++)
	{
		if (component.childNodes[i].nodeName == '#text') 
		{
			continue;
		}

		if (component.childNodes[i].id && component.childNodes[i].id.match(/(_header$|^header_|_header_)/))
		{
			return component.childNodes[i];
		}
	}

	return null;
}

