/*
 * Drag & Drop
 * event list:
 * 		- onDragEnd
 * 		- onDragStart
 * 		- onOverDropTarget
 * 		- onBorder
 * 		- onDrag
 *
 * style:
 * 		- .cursor-move *			{ cursor: move !important; }
 * 		- .cursor-not-allowed *		{ cursor: not-allowed !important; }
 */

var CURRENT_DRAG_DROP_OBJECT = null;

function dragAndDrop()
{
	this.dropTargetList		= new Array();		// ahova lehet mozgatni az aktuális elemet
	this.assignTargetList	= new Array();		// ezekhez az elemekhez lett hozzárendelve a drag funkció
	this.dropTarget			= null;				// ami megvan adva a dropTargetList-ben azok közül az az objektum ami fölöt éppen a cursor van
	this.dragObject			= null;				// aktuális elem
	this.cloneObject		= null;				// ezt az objektumot mozgatja
	this.cloneObjectBounds	= {_x:0, _y:0, width:0, height: 0};
	this.validTarget		= false;			// le lehet-e itt dobni az aktuális elemet
	this.addEvent			= addEvent;			// eventhandler
	this.drag				= false;			// megkezdődhet-e a mozgatás
	this.sensitive			= 2;				// mekkora egértevékenységtől kezdje a mozgatást
	this.startMouse			= new Object();		// egér kezdeti kordinátái
	this.startObject		= new Object();		// objektum kezdeti kordinátái
	this.onDragEnd			= function(){};		// event - ha vége a dragnak akkor hívódik meg (param: this)
	this.onDragStart		= function(){};		// event - ha megkezdődik a drag akkor hívódik meg (param: this)
	this.onOverDropTarget	= function(){};		// event - ha valamelyik dropTarget fölött van az aktuális objektum ez a fügvény folyamatosan hívodik ha mozgatják az objektumot
	this.onBorder			= function(){};		// event - ha eléri valamelyik határt akkor folyamatosan meghívódik amikor mozgatják az objektumot
	this.onDrag				= function(){};		// event - objektum mozgatása közben/alatt hívódik meg
	this.axis				= {_x:true,_y:true};// melyik tengelyeken müködjön a drag
	this.noClone			= false;			// ha true akkor az eredetit mozgatja és nem egy másolatot
	this.uproot				= false;			// ha a noClone true ennek akkor van szerepe, Ha true akkor az objektumot kiszakítja az eredeti környezetéből és azt mozgatja tovább
	this.alpha				= 90;				// a mozgatott objektum alfája
	this.assignNumber		= 0;				// hány darab objecthez lett hozzárendelve ez az objektum
	this.cursor				= {drag:'cursor-move', noDrop:'cursor-not-allowed'};// egér cursor ikonok class nevei
	this.border				= {_x:0,_y:0,width:null,height:null};				// határok amin kívülre nem lehet húzni az adott objektumot
	this.borderTest			= {border:'', hit:false};	// ha ütközött valamilyek oldalhoz akkkor a hit true és a border értéke hogy melyikhez ütközik: L,T,R,B,LT,LB,RT,RB
	this.enableDrag			= true;
	this.distance			= {_x:0,_y:0};
	this._clusterList		= new HTMLNodeList(new Array());		// ha egy html nodeokból álló csoportot adunk meg mozgatás céljából akkor ez tartalmazza az elemek listáját
	this.delClone			= true;
	
	this.addEvent(document, 'mousemove', null, this.handlerMouseMove);
	this.addEvent(document, 'mouseup', null, this.handlerMouseUp);
}

dragAndDrop.prototype.getClusterList = function()
{
	if(isNodeList(this._clusterList))
	{
		if(this._clusterList.length)
			return this._clusterList;
		else
			return new HTMLNodeList([this.dragObject]);
	}
	return new HTMLNodeList(new Array());
}

dragAndDrop.prototype.startDrag = function(mouseEvent)
{
	if(isFunction(this.onDragStart)) this.onDragStart(mouseEvent);
	
	this.refreshDropTarget();
	this.refreshBorder();
	
	if(this.cloneObject)
	{
		this.cloneObject.setStyle({filter:'alpha(opacity='+this.alpha+')', opacity:this.alpha/100});
	}	
}

dragAndDrop.prototype.endDrag = function(mouseEvent)
{
	this.enableSelection();
	this.changeMouseStyle(true);
	
	if(isFunction(this.onDragEnd) && this.drag) this.onDragEnd(mouseEvent);
	if(this.cloneObject && !this.noClone && this.delClone) this.cloneObject.del();
	
	this.startMouse 		= new Object();
	this.startObject		= new Object();
	if(!this.noClone)this.cloneObject = null;
	this.drag				= false;
	this.validTarget		= false;
	this.dropTarget			= null;
	
	if(this.cloneObject)
	{
		this.cloneObject.setStyle({filter:'alpha(opacity=100)', opacity:1});
	}
}

/**
 * @param obj {HTMLElement} Ez az objektum indítja a drag&drop funkciót
 * @param className {String} Az objektumon belül azok kerülnek be ebbe a listába aminek ez a class neve
 * @param tagName {String} Az objektumon belül azok kerülnek bele ebbe a listába aminek ez a tag neve ( ha class és tag név is megvan adva akkor mindkét szűkítés érvényes lesz)
 * @param drag {HTMLElement} Ez nem fontos,de ha megvan adva akkor ezt fogja mozgatni, ha nincs megadva akkor azt ami az első paraméterben megvolt adva
 */
dragAndDrop.prototype.assign = function(obj, filter, drag)
{
	if(!isElement(obj)) return false;
	
	var list = getElementsByFilter(obj, filter);
	
	if(!list.length)
	{
		var list = new Array();
		list.push(obj);
	}
	
	this.assignNumber = list.length;
	for(var i=0; i<list.length ; i++)
	{
		obj = list[i];
		this.assignTargetList.push(obj);
		
		if(!isElement(drag))
			this.dragObject = obj;
		else
			this.dragObject = drag;
		
		obj.dragAndDrop = this;
		if(getBrowser().ie)obj.setAttribute('dragObject', 'true');
		this.addEvent(obj, 'mousedown', null, this.handlerMouseDown);		
	}
	return true;
}

dragAndDrop.prototype.clone = function()
{
	var bounds	= getBounds(this.dragObject);
	
	if(!this.noClone)
	{
		var e 		= create.div(null, null, null, {position:'absolute', width:bounds.width+'px', height:bounds.height+'px', left:bounds._x+'px', top:bounds._y+'px'}, document.getElementsByTagName('body').item(0));
		var copy	= fullCloneElement(this.dragObject);		
		
		e.appendChild(copy);
		
		this.cloneObject 			= e;
		this.cloneObject.original 	= copy.original;
		this.cloneObject.addClassName('drag-object');
	}
	else
	{
		var dragOb = this.dragObject;
		if(this.uproot)
		{
			var b = getBounds(this.dragObject);
			dragOb = document.getElementsByTagName('body').item(0).appendChild(this.dragObject);
			dragOb.style.left		= b._x+'px';
			dragOb.style.top		= b._y+'px';
			dragOb.style.position	= 'absolute';
			
			this.cloneObject = dragOb;
			this.cloneObject.original = dragOb;
			this.startObject = getBounds(dragOb);
		}
		else
		{
			this.cloneObject = dragOb;
			this.cloneObject.original = dragOb;
			
			this.cloneObject.style.left 	= this.cloneObject.offsetLeft+'px';
			this.cloneObject.style.top 		= this.cloneObject.offsetTop+'px';
			this.cloneObject.style.position = 'absolute';
		}			
	}
}

dragAndDrop.prototype.createCluster = function(obj, filter)
{
	if(!this.noClone)
	{
		if(!isElement(obj)) return false;
		var list = getElementsByFilter(obj, filter);
		if(!list.length) return false;
		
		this._clusterList = list;
		
		var cloneObject		= create.div(null, null, null, {position:'absolute'}, document.getElementsByTagName('body').item(0));
		var cloneWidth		= 0;
		var cloneHeight		= 0;
		var cloneLeft		= 0;
		var cloneTop		= 0;
		
		var startTop 	= list.item(0).offsetTop;
		var startLeft	= list.item(0).offsetLeft;
		
		for(var i=0 ; i<list.length ; i++)
		{
			var elm 			= list.item(i);
			var fullCloneElm 	= fullCloneElement(elm);
			if(i > 0)
			{
				
				var top 					= elm.offsetTop - startTop;
				var left					= elm.offsetLeft - startLeft;
				fullCloneElm.style.left 	= left+'px';
				fullCloneElm.style.top		= top+'px';
				fullCloneElm.style.position = 'absolute';
			}
			else
			{
				var top 	= 0;
				var left	= 0;
			}
			cloneWidth = Math.max(cloneWidth, left);
			cloneHeight = Math.max(cloneHeight, top);
			cloneObject.appendChild(fullCloneElm);
		}
		var bounds = getBounds(this.dragObject);
        cloneHeight += bounds.height;
        cloneWidth 	+= bounds.width;
		cloneLeft	= bounds._x-this.dragObject.offsetLeft+startLeft;
		cloneTop	= bounds._y-this.dragObject.offsetTop+startTop;		
		
		cloneObject.setStyle({width:cloneWidth+'px', height:cloneHeight+'px', left:cloneLeft+'px', top:cloneTop+'px'});
		this.setDragObject(cloneObject);
		return true;
	}
	return false;
}

dragAndDrop.prototype.checkValidTarget = function(event)
{
	if(this.dropTargetList.length == 0)
	{
		this.dropTarget	 = null;
		this.validTarget = true;	
		return true;
	}
	
	//var drag		= this.cloneObjectBounds;
	var drag		= null;
	var mouse		= null;
		
	for(var i=0 ; i<this.dropTargetList.length ; i++)
	{
		var curr = this.dropTargetList[i];
		 
		if(curr.type == 'object')
		{
			if(drag == null) drag = getBounds(this.cloneObject);
			if(boxIntercept(curr.bounds, drag))
			{
				this.dropTarget		= curr.obj;
				this.dropTarget.cb	= curr.cb;
				this.validTarget 	= true;
				if(isObject(curr.cb))
				{
					var _OBJ = isObject(curr.cb.obj) ? curr.cb.obj : window;
					if(isFunction(_OBJ[curr.cb.method])) this.validTarget = _OBJ[curr.cb.method](this, curr.cb.param);
					else if(curr.cb.event) this.validTarget = _OBJ.fireEvent(curr.cb.event,{dragAndDrop:this,sourceComponent:_OBJ},false);
				}
				return this.validTarget;
			}
		}
		else if(curr.type == 'mouse')
		{
			if(mouse == null) mouse = getMouseCoords(event);
			if(pointInBox(curr.bounds, mouse))
			{
				this.dropTarget		= curr.obj;
				this.dropTarget.cb	= curr.cb;
				this.validTarget 	= true;
				if(isObject(curr.cb))
				{
					var _OBJ = isObject(curr.cb.obj) ? curr.cb.obj : window;
					if(isFunction(_OBJ[curr.cb.method])) this.validTarget = _OBJ[curr.cb.method](this, curr.cb.param);
				}
				return this.validTarget;
			}
		}
		
	}
	
	this.dropTarget		= null;
	this.validTarget 	= false;
	return false;
}

dragAndDrop.prototype.changeMouseStyle = function(remove)
{
	var body = document.getElementsByTagName('body').item(0);
	if(!remove)
	{
		if(this.validTarget)
		{
			body.delClassName(this.cursor.noDrop);
			body.addClassName(this.cursor.drag);	
		}
		else
		{
			body.delClassName(this.cursor.drag);
			body.addClassName(this.cursor.noDrop);	
		}
	}
	else
	{	
		body.delClassName(this.cursor.drag);
		body.delClassName(this.cursor.noDrop);
	}
}

/**
 * @param obj {HTMLElement} Lehetséges hely hozzáadása, hogy lehessen leejteni az objektumot
 * @param className {String} Az objektumon belül azok kerülnek be ebbe a listába aminek ez a class neve
 * @param tagName {String} Az objektumon belül azok kerülnek bele ebbe a listába aminek ez a tag neve ( ha class és tag név is megvan adva akkor mindkét szűkítés érvényes lesz)
 * @param type {String} Az ellenőrzés típúsa : mouse( egérkordinátát vizsgálja ), object ( az objektum határait vizsgálja )
 */
dragAndDrop.prototype.addDropTarget = function(obj, filter, type, cb, offset)
{
	if(isElement(obj))
	{
		type = (isUndefined(type) ? 'object' : type);
		
		list = getElementsByFilter(obj, filter);
		if(!list.length)
		{
			var list = new Array();
			list.push(obj);
		}
		
		for(var i=0; i<list.length ; i++)
		{
			var bound = getBounds(list[i]);
			if(isObject(offset))
			{
				if(offset._x)		bound._x 		+= offset._x;
				if(offset._y)		bound._y 		+= offset._y;
				if(offset.width)	bound.width 	+= offset.width;
				if(offset.height)	bound.height 	+= offset.height;
			}
			this.dropTargetList.push({obj:list[i], bounds:bound, type:type, cb:cb});
		}
	}
}

dragAndDrop.prototype.setBorder = function(param, offset)
{
	if(isElement(param)) 	this.border = getBounds(param);
	else
	if(isObject(param))		this.border = param;	
	if(isObject(offset))
	{
		if(offset._x)		this.border._x 		+= offset._x;
		if(offset._y)		this.border._y 		+= offset._y;
		if(offset.width)	this.border.width 	+= offset.width;
		if(offset.height)	this.border.height 	+= offset.height;
	}
}

dragAndDrop.prototype.setDragObject = function(obj)
{
	if(isElement(this.cloneObject) && !this.noClone) this.cloneObject.del();
	this.cloneObject = obj;
	this.startObject = getBounds(obj);
}

dragAndDrop.prototype.refreshDropTarget = function()
{
	for(var k=0; k<this.dropTargetList.length ; k++)
	{
		this.dropTargetList[k].bounds = getBounds(this.dropTargetList[k].obj);		
	}
}

dragAndDrop.prototype.refreshBorder = function()
{
	if(isElement(this.border.obj))
		this.border = getBounds(this.border.obj);
}

dragAndDrop.prototype.removeAllEvent = function()
{
	removeEvent(document, 'mousemove', null, this.handlerMouseMove);
	removeEvent(document, 'mouseup', null, this.handlerMouseUp);
	
	for(var i=0 ; i<this.assignTargetList.length ; i++)
	{
		removeEvent(this.assignTargetList[i], 'mousedown', null, this.handlerMouseDown);
	}	
}

dragAndDrop.prototype.handlerMouseMove = function(event)
{
	if(CURRENT_DRAG_DROP_OBJECT !== null && isObject(CURRENT_DRAG_DROP_OBJECT) && CURRENT_DRAG_DROP_OBJECT.enableDrag)
	{
		var mouse = getMouseCoords(event);
		var dragAndDrop = CURRENT_DRAG_DROP_OBJECT;
		if(!dragAndDrop.drag)
		{
			var sm 		= dragAndDrop.startMouse;
			var sens	= dragAndDrop.sensitive;
			if( (sm._x - sens >= mouse._x || sm._x + sens <= mouse._x) || (sm._y - sens >= mouse._y || sm._y + sens <= mouse._y) )
			{
				dragAndDrop.drag = true;
				if(dragAndDrop.cloneObject) dragAndDrop.startDrag(event);
			}	
		}
		else if(isElement(dragAndDrop.cloneObject))
		{
			var sm 		= dragAndDrop.startMouse;
			var so		= dragAndDrop.startObject;
			var dist 	= new Object();
				dist._x	= sm._x - mouse._x;
				dist._y	= sm._y - mouse._y;
			dragAndDrop.distance = dist;
			
			var newLeft	= so._x - dist._x;
			var newTop	= so._y - dist._y;
			var border	= dragAndDrop.border;
			dragAndDrop.borderTest = {border:'', hit:false}
			
			if(border._x !== null && newLeft <= border._x){ newLeft = border._x; dragAndDrop.borderTest.border+= 'L'; dragAndDrop.borderTest.hit = true };
			if(border._x !== null && border.width !== null && newLeft >= border.width + border._x - so.width){ newLeft = border.width + border._x - so.width; dragAndDrop.borderTest.border+= 'R'; dragAndDrop.borderTest.hit = true}
			
			if(border._y !== null && newTop <= border._y){ newTop = border._y; dragAndDrop.borderTest.border+= 'T'; dragAndDrop.borderTest.hit = true }
			if(border._y !== null && border.height !== null && newTop >= border.height + border._y - so.height){ newTop = border.height + border._y - so.height; dragAndDrop.borderTest.border+= 'B'; dragAndDrop.borderTest.hit = true }
			
			if(dragAndDrop.axis._x)dragAndDrop.cloneObject.style.left = newLeft + 'px';
			if(dragAndDrop.axis._y)dragAndDrop.cloneObject.style.top = newTop + 'px';
			
			if(dragAndDrop.checkValidTarget(event))
				dragAndDrop.onOverDropTarget(event);
				
			if(dragAndDrop.borderTest.hit)
				dragAndDrop.onBorder(event);
				
			dragAndDrop.onDrag(dragAndDrop);
			
			dragAndDrop.changeMouseStyle();			
		}
		else
		{
			dragAndDrop.clone();
			dragAndDrop.startDrag(event);
		}
	}
}
/*UGLY BY IE!!! :S*/
dragAndDrop.prototype.handlerMouseDown = function(event)
{
	if(getBrowser().ie)
	{
		var target = event.srcElement;
		while(target && target.getAttribute('dragObject') != 'true')
		{
			target = target.parentNode;
		}
		CURRENT_DRAG_DROP_OBJECT = target.dragAndDrop;
		
		if(CURRENT_DRAG_DROP_OBJECT.assignNumber > 1)
			if(!CURRENT_DRAG_DROP_OBJECT.noClone)CURRENT_DRAG_DROP_OBJECT.dragObject = target;
		else
			if(!CURRENT_DRAG_DROP_OBJECT.noClone)CURRENT_DRAG_DROP_OBJECT.dragObject = target.dragObject;
	}
	else
	{
		CURRENT_DRAG_DROP_OBJECT = this.dragAndDrop;
		if(CURRENT_DRAG_DROP_OBJECT.assignNumber > 1)
			if(!CURRENT_DRAG_DROP_OBJECT.noClone)CURRENT_DRAG_DROP_OBJECT.dragObject = this;
		else
			if(!CURRENT_DRAG_DROP_OBJECT.noClone)CURRENT_DRAG_DROP_OBJECT.dragObject = this.dragObject;
	}
	
	if( !(CURRENT_DRAG_DROP_OBJECT instanceof dragAndDrop) ) return false;
	
	CURRENT_DRAG_DROP_OBJECT.disableSelection();
	CURRENT_DRAG_DROP_OBJECT.startMouse 	= getMouseCoords(event);
	
	
	
	if(!CURRENT_DRAG_DROP_OBJECT.noClone)
		var bounds = getBounds(CURRENT_DRAG_DROP_OBJECT.dragObject);
	else
		var bounds = {_x:CURRENT_DRAG_DROP_OBJECT.dragObject.offsetLeft, _y:CURRENT_DRAG_DROP_OBJECT.dragObject.offsetTop, width:CURRENT_DRAG_DROP_OBJECT.dragObject.offsetWidth, height:CURRENT_DRAG_DROP_OBJECT.dragObject.offsetHeight}
	
	CURRENT_DRAG_DROP_OBJECT.startObject = bounds;	
}

dragAndDrop.prototype.handlerMouseUp = function(event)
{
	if(CURRENT_DRAG_DROP_OBJECT instanceof dragAndDrop)
	{
		if(isFunction(CURRENT_DRAG_DROP_OBJECT.endDrag))
			CURRENT_DRAG_DROP_OBJECT.endDrag(event);
		CURRENT_DRAG_DROP_OBJECT = null;
	}
}

dragAndDrop.prototype.disableSelection = function()
{
	disableSelection(document.getElementsByTagName('body').item(0));
}

dragAndDrop.prototype.enableSelection = function()
{
	enableSelection(document.getElementsByTagName('body').item(0));
}

dragAndDrop.prototype.drawDropTargetBounds = function()
{
	var list = this.dropTargetList;
	if(list.length)
	{
		for(var k=0 ; k<list.length ; k++)
		{
			create.div(null, 'x:'+list[k].bounds._x+'<br />y:'+list[k].bounds._y+'<br />w:'+list[k].bounds.width+'<br />h:'+list[k].bounds.height+'<br />', null, {position:'absolute', left:list[k].bounds._x+'px', top:list[k].bounds._y+'px', width:list[k].bounds.width+'px', height:list[k].bounds.height+'px', backgroundColor:'#80FFFF', opacity:0.5, filter:'alpha(opacity=50)', fontSize:'12px', fontWeight:'bold', color:'#000000'}, document.getElementsByTagName('body').item(0));
		}
	}
}

dragAndDrop.prototype.drawBorder = function()
{
	if(this.border)
	{
		var styleLeft	= { left:this.border._x+'px', top:this.border._y+'px', width:'0px', height:this.border.height+'px', position:'absolute', borderRight:'2px dashed #ffd200', backgroundColor:'#333333' };
		var styleTop	= { left:this.border._x+'px', top:this.border._y+'px', width:this.border.width+'px', height:'0px', position:'absolute', borderBottom:'2px dashed #ffd200', backgroundColor:'#333333' };
		var styleRight	= { left:this.border._x+this.border.width+'px', top:this.border._y+'px', width:'0px', height:this.border.height+'px', position:'absolute', borderLeft:'2px dashed #ffd200', backgroundColor:'#333333' };
		var styleBottom	= { left:this.border._x+'px', top:this.border._y+this.border.height+'px', width:this.border.width+'px', height:'0px', position:'absolute', borderTop:'2px dashed #ffd200', backgroundColor:'#333333' };
		
		create.div(null, null, null, styleLeft, document.getElementsByTagName('body').item(0));
		create.div(null, null, null, styleTop, document.getElementsByTagName('body').item(0));
		create.div(null, null, null, styleRight, document.getElementsByTagName('body').item(0));
		create.div(null, null, null, styleBottom, document.getElementsByTagName('body').item(0));
	}
}

dragAndDrop.prototype.destroy = function()
{
	this.removeAllEvent();	
}