/*	VERSION: 2.3

USAGE:
	#include "functions/dragAndDrop.as"
	makeDragAndDrop( this, "dragHandler", nextDepth(this) );		// all parameters are optional

For each movieClip/component that will send drag actions:
	drag = _parent.dragHandler_mc.makeDraggable( this );
	drag.onSendClick = function( dragHandler_mc ){
		return {data:data, image:image, imageOffset:imageOffset};
	};
	drag.onSendDrag = function( dragHandler_mc ){
		return {data:data, image:image, imageOffset:imageOffset};
	};
	drag.onSendDrop = function( dropTarget, isValid ){};
	drag.onRecieveDrop = function( otherDrag_obj ){
		return isValid;  // if this drop is accepted
	};
	
	
	Drop-targets only need this:
		drag = _parent.dragHandler_mc.makeDraggable( this );
		drag.onRecieveDrop = function( dragData ){
			return true;
		);
	
	
	
DEFAULT SETUP:  (when no parameters are passed)
	makeDragAndDrop( this, dragHandler_newDepth, nextDepth(this) );
	
	
	
NOTES:
	Both sending and recieving movieClips need their own drag objects,
	otherwise, they cannot communicate.
	Drag objects are created using the makeDraggable() function.
	
	onSendClick() should clear data, otherwise, previous drag data will still exist
	
	MovieClips can recieve drops from themselves.
	
	MovieClips can send data to undefined  (dropTarget can be undefined)  (dropped on nothing)
	
	Irrelevent areas of movieClips and components should explicitly send empty data
	by clearing their dragData.data variable.
	(usually within their onSendClick() or onSendDrag() functions)
	
	Be prepared to recieve undefined data from other movieClips.
	Typically, this will occur when the user clicks a drop-target and drags to a movieClip that has drag-&-drop.
	
	
	
CONTENTS OF dragData:
	parent:movieClip,			// hitTest click-target for this set of drag events
	data:{},							// arbitrary data to send to the drop target
	isClicked:false,			// whether parent movieClip is being pressed
	isDragging:false,			// whether this drag is active
	clickPoint:Point,			// where the click occured (global-relative)
	dragPoint:Point,			// where the drag started occuring  (global-relative)
	dropPoint:Point,			// where the drop occured (global-relative)
	onSendClick:Function,	// reaction to clicking on the parent
	onSendDrag:Function,	// reaction when the drag begins
	onRecieveDrop:Function,		// reaction to recieving a drop from another movieClip
	onSendDrop:Function		// reaction when the drop occurs
	
	The functions are defined externally, as demonstrated in the usage example above.
	The return data sent by onSendClick and onSendDrag are all optional.
*/


#include "nextDepth.as"
makeDragAndDrop = function( parent, newName, newDepth )
{
	// resolve optional parameters
	var parent = parent || this;
	var newDepth = (newDepth!=undefined) ? newDepth : nextDepth(parent);
	var newName = newName || "dragHandler_"+newDepth;
	// create container
	_this = parent.createEmptyMovieClip( newName, newDepth );
	_this._visible = false;
	_this.enabled = true;
	_this.preview = _this.createEmptyMovieClip("preview", 0);
	_this.dragList = [];
	_this.dragThreshold = 8;
	
	
	
	// __________________________________________________
	_this.makeDraggable = function( movieClip )
	{
		var newDrag = {
			parent:movieClip,							// hitTest click-target for this set of drag events
			data:{},											// arbitrary data to send to the drop target
			isClicked:false,							// whether parent movieClip is being pressed
			isDragging:false,							// whether this drag is active
			clickPoint:Point,							// where the clicked occured (global-relative)
			dragPoint:Point,							// where the drag started occuring  (global-relative)
			dropPoint:Point,							// where the drop occured (global-relative)
			onSendClick:function(){},			// reaction to clicking on the parent
			onSendDrag:function(){},			// reaction when the drag begins
			onRecieveDrop:function(){},		// reaction to recieving a drop from another movieClip
			onSendDrop:function(){}				// reaction when the drop occurs
		}
		_this.dragList.push( newDrag );
		return newDrag;
	}// makeDraggable()
	_this.removeDrag = function( movieClip )
	{
		for(var i in _this.dragList)
			if(_this.dragList[i].parent == movieClip)
				_this.dragList.splice( i, 1 );
	}// removeDrag()
	
	
	
	// __________________________________________________
	_this.mouse = {
		onMouseDown:function()
		{
			_this.checkCleanUp();
			// find the movieClip that's been clicked on 
			for(var i=0; i<_this.dragList.length; i++)
			{// for:  each drag object
				var drag_obj = _this.dragList[i];
				// ignore disabled movieClips / components
				if(	drag_obj.parent._visible == false
				||	drag_obj.parent.enabled == false
				||	drag_obj.parent.visible == false)
					drag_obj = null;		// ignore it
				
				if( drag_obj.parent.hitTest(_root._xmouse, _root._ymouse, true) )
				{// if:  this movieClip was clicked
					// remember that this movieClip was clicked on
					drag_obj.isClicked = true;
					// remember the click coordinates (global-relative)
					drag_obj.clickPoint = new flash.geom.Point(drag_obj.parent._xmouse,drag_obj.parent._ymouse);
					drag_obj.parent.localToGlobal( drag_obj.clickPoint );
					// tell the sender to prepare data for sending
					_this.image = null;
					var newData = drag_obj.onSendClick( _this );
					// add data
					for(var nam in newData.data)
						drag_obj.data[nam] = newData.data[nam];
					if(newData.imageOffset)
						_this.imageOffset = newData.imageOffset;
					if(newData.image!=undefined)
						_this.image = newData.image;
				}// if:  this movieClip was clicked
			}// for:  each drag object
		},// click
		
		
		onMouseMove:function()
		{
			_this.checkCleanUp();
			// find the movieClip that was clicked
			for(var i=0; i<_this.dragList.length; i++)
			{// for:  each drag object
				var drag_obj = _this.dragList[i];
				// ignore disabled movieClips / components
				if(	drag_obj.parent._visible == false
				||	drag_obj.parent.enabled == false
				||	drag_obj.parent.visible == false)
					drag_obj = null;		// ignore it
				
				if( drag_obj.isClicked==true )
				{// if: this movieClip was clicked on
					if(drag_obj.isDragging==false)
					{// if: clicked movieClip hasn't started dragging yet
						// remember where this movieClip started being dragged
						drag_obj.dragPoint = new flash.geom.Point(drag_obj.parent._xmouse,drag_obj.parent._ymouse);
						drag_obj.parent.localToGlobal( drag_obj.dragPoint );
						// check whether it's been dragged far enough
						var dragDistance = flash.geom.Point.distance( drag_obj.clickPoint, drag_obj.dragPoint );
						if(dragDistance > _this.dragThreshold)
						{// if:  the movieClip has been dragged far enough to count as deliberate dragging
							drag_obj.isDragging = true;
							// tell the sender that a drag has begun
							var newData = drag_obj.onSendDrag( _this );
							// add data
							for(var nam in newData.data)
								drag_obj.data[nam] = newData.data[nam];
							if(newData.imageOffset)
								_this.imageOffset = newData.imageOffset;
							if(newData.image!=undefined)
								_this.image = newData.image;
							// appear and start following the mouse
							_this._x = _this._parent._xmouse;
							_this._y = _this._parent._ymouse;
							_this._visible = true;
						}// if:  the movieClip has been dragged far enough to count as deliberate dragging
					}// if: clicked movieClip hasn't started dragging yet
					
					if(drag_obj.isDragging==true)
					{// if: clicked movieClip is being dragged
						// follow the mouse
						_this._x = _this._parent._xmouse;
						_this._y = _this._parent._ymouse;
						updateAfterEvent();
					}// if: clicked movieClip is being dragged
				}// if: this movieClip was clicked on
			}// for:  each drag object
		},// drag
		
		
		onMouseUp:function()
		{
			_this.checkCleanUp();
			for(var a=0; a<_this.dragList.length; a++)
			{// for:  each drag object
				var drag_obj = _this.dragList[a];
				
				if(drag_obj.isDragging == true)
				{// if: this object is being dragged
					var isValid = false;
					var drop_obj = undefined;
					var foundDropTarget = false;
					// hitTest against registered movieclips
					for(var b=0; b<_this.dragList.length; b++)
					{// for:  each drag object
						drop_obj = _this.dragList[b];
						// ignore disabled movieClips / components
						if(	drop_obj.parent._visible == false
						||	drop_obj.parent.enabled == false
						||	drop_obj.parent.visible == false)
							drop_obj = null;		// ignore it
						
						if(drop_obj.parent.hitTest(_root._xmouse, _root._ymouse, true) )
						{// if:  dropped on this object
							drop_obj.dropPoint = new flash.geom.Point(_root._xmouse, _root._ymouse);
							// tell the target that it's recieving something
							isValid = drop_obj.onRecieveDrop( drag_obj );
							foundDropTarget = true;
						}// if:  dropped on this object
						// only affect one drop-target
						if(foundDropTarget)
							break;
					}// for:  each drag object
					// detect / invalid empty drop-targets
					if(!foundDropTarget)
						drop_obj = null;
					// tell the sender that the data has been sent, what recieved it, and whether it was accepted
					drag_obj.onSendDrop( drop_obj.parent, isValid );
				}// if: this object is being dragged
				// stop dragging
				drag_obj.isClicked = false;
				drag_obj.isDragging = false;
			}// for:  each drag object
			// disappear
			_this._visible = false;
		}// drop
	}// mouse listener object
	//Mouse.addListener( _this.mouse );
	
	
	
	// __________________________________________________
	// "enabled" property
	var init_enabled = (_this.enabled!=undefined) ? _this.enabled : true;
	_this._enabled = null;
	_this.get_enabled = function(){
		return _enabled;
	}
	_this.set_enabled = function( newValue )
	{
		_enabled = newValue;
		Mouse.removeListener( _this.mouse );
		if(newValue)
			Mouse.addListener( _this.mouse );
	}
	_this.addProperty( "enabled", _this.get_enabled, _this.set_enabled );
	_this.enabled = init_enabled;
	
	
	
	var init_imageOffset = _this.imageOffset || new flash.geom.Point(0,0);
	_this._imageOffset = null;
	_this.get_imageOffset = function(){
		return _imageOffset;
	}
	_this.set_imageOffset = function( newValue )
	{
		_imageOffset = newValue;
		_this.preview._x = -_imageOffset.x;
		_this.preview._y = -_imageOffset.y;
	}
	_this.addProperty( "imageOffset", _this.get_imageOffset, _this.set_imageOffset );
	_this.imageOffset = init_imageOffset;
	
	
	
	// "image" property
	var init_image = _this.image;
	_this._image = null;
	_this.imageOffset = new flash.geom.Point(0,0);
	_this.get_image = function(){
		return _image;
	}
	_this.set_image = function( newBitmap ){
		if(newBitmap == null)
			var newBitmap = new flash.display.BitmapData( 1,1, true, 0 );
		_this.preview.attachBitmap( newBitmap, 0 );
		_image = newBitmap;
	}
	_this.addProperty( "image", _this.get_image, _this.set_image );
	_this.image = init_image;
	
	
	
	// __________________________________________________
	// clean-up
	onUnload = function(){
		Mouse.removeListener( _this.mouse );
	}
	unload = onUnload;
	checkCleanUp = function()
	{
		if(_this._name == undefined)
			_this.onUnload();
	}// checkCleanUp()
	
	
	
	return _this;
}// makeDragAndDrop()