/* Copyright (c) 2005 Tim Taylor Consulting (see LICENSE.txt) */

ToolMan._dragFactory = {
	createSimpleGroup : function(element, proxyElement, cbQueryDrop) {
		handle = element ;
		var group = this.createGroup(element, proxyElement, cbQueryDrop) ;
		group.setHandle(handle) ;		
		group.onTopWhileDragging() ;
		return group ;
	},


   createProxy : function(element, proxyElement, cbStartDrag, cbQueryDrop, cbQueryMove ) {
		handle = proxyElement ;
		var group = this.createGroup(element, proxyElement, cbStartDrag, cbQueryDrop, cbQueryMove) ;
		group.setHandle(proxyElement) ;		
		group.onTopWhileDragging() ;
		return group ;
	},



	createGroup : function(element, proxyElement, cbStartDrag, cbQueryDrop, cbQueryMove) 
   {
      var group = new _ToolManDragGroup(this, element, proxyElement)
//debugger;
		var position = ToolMan.css().readStyle(element, 'position');
		   if (position == 'static') {
			   //element.style["position"] = 'relative' ;
		   } else if (position == 'absolute') {
			   /* for Safari 1.2 */
            if(element)
			      ToolMan.coordinates().topLeftOffset(element).reposition(element) ;         
		   }

       position = ToolMan.css().readStyle(proxyElement, 'position');
         if (position == 'static') 
         {
			   //proxyElement.style["position"] = 'relative' ;
		   } else if (position == 'absolute') {
			   /* for Safari 1.2 */
			   ToolMan.coordinates().topLeftOffset(proxyElement).reposition(proxyElement) ;
         }


		// TODO: only if ToolMan.isDebugging()
		group.register('draginit', this._showDragEventStatus) ;
      if(cbQueryMove)
         group.register('dragmove', cbQueryMove) ;
      else
		  group.register('dragmove', this._showDragEventStatus) ;
		if(cbQueryDrop) 
		   group.register('dragend', cbQueryDrop) ;
      if(cbStartDrag) 
		   group.register('dragstart', cbStartDrag) ;

		return group ;
	},

	_showDragEventStatus : function(dragEvent) {
		window.status = dragEvent.toString()
	},

	constraints : function() {
		return this._constraintFactory
	},

	_createEvent : function(type, event, group) {
		return new _ToolManDragEvent(type, event, group)
	}
}

//element may be null (for new Resize functionality) but does not hurt this function.
function _ToolManDragGroup(factory, element, proxyElement) {
	this.factory = factory;
	this.element = element;
   this.proxyElement = proxyElement;
	this._handle = null;
	this._thresholdDistance = 3;
	this._transforms = new Array();
	// TODO: refactor into a helper object, move into events.js
	this._listeners = new Array();
	this._listeners['draginit'] = new Array();
	this._listeners['dragstart'] = new Array();
	this._listeners['dragmove'] = new Array();
	this._listeners['dragend'] = new Array();
}

_ToolManDragGroup.prototype = {
	/*
	 * TODO:
	 *   - unregister(type, func)
	 *   - move custom event listener stuff into Event library
	 *   - keyboard nudging of "selected" group
	 */

	setHandle : function(handle) {
		var events = ToolMan.events()

		handle.toolManDragGroup = this
		events.register(handle, 'mousedown', this._dragInit)
		handle.onmousedown = function() { return false }

		if (this.element && this.element != handle)
			events.unregister(this.element, 'mousedown', this._dragInit)
	},

	register : function(type, func) {
		this._listeners[type].push(func)
	},

	addTransform : function(transformFunc) {
		this._transforms.push(transformFunc)
	},

	verticalOnly : function() {
		this.addTransform(this.factory.constraints().vertical())
	},

	horizontalOnly : function() {
		this.addTransform(this.factory.constraints().horizontal())
	},

	setThreshold : function(thresholdDistance) {
		this._thresholdDistance = thresholdDistance
	},

	transparentDrag : function(opacity) {
      
      if(null == this.element) return ;
      
		var opacity = typeof(opacity) != "undefined" ? opacity : 0.75;
		var originalOpacity = ToolMan.css().readStyle(this.element, "opacity") ;

		this.register('dragstart', function(dragEvent) {
			var element = dragEvent.group.element ;
			element.style.opacity = opacity ;
			element.style.filter = 'alpha(opacity=' + (opacity * 100) + ')' ;
		})
		this.register('dragend', function(dragEvent) {
			var element = dragEvent.group.element ;
			element.style.opacity = originalOpacity ;
			element.style.filter = 'alpha(opacity=100)' ;
		})
	},

	onTopWhileDragging : function(zIndex) {
	//debugger ;
      if(null == this.element) return ;
		var zIndex = typeof(zIndex) != "undefined" ? zIndex : 65534;
//bug: 		ToolMan.css().readStyle(this.element, "z-index") 
// would yield undefined int IE
		var originalZIndex = ToolMan.css().readStyle(this.element, "z-index")
    
		this.register('dragstart', function(dragEvent) {
			dragEvent.group.element.style.zIndex = zIndex ;
		})
		this.register('dragend', function(dragEvent) {
		  //dragEvent.group.element.style.zIndex = originalZIndex
		})
	},

	_dragInit : function(event) {
		event = ToolMan.events().fix(event) ;
		var group = document.toolManDragGroup = this.toolManDragGroup ;
		var dragEvent = group.factory._createEvent('draginit', event, group) ;

		group._isThresholdExceeded = false ;
		group._initialMouseOffset = dragEvent.mouseOffset ;
		group._grabOffset = dragEvent.mouseOffset.minus(dragEvent.topLeftOffset);
		ToolMan.events().register(document, 'mousemove', group._drag);
document.onmousemove = function() { return false ; }
		ToolMan.events().register(document, 'mouseup', group._dragEnd) ;

		group._notifyListeners(dragEvent)
	},

	_drag : function(event) {
		event = ToolMan.events().fix(event)
		var coordinates = ToolMan.coordinates()
		var group = this.toolManDragGroup
		if (!group) return ;
		var dragEvent = group.factory._createEvent('dragmove', event, group) ;

		var newTopLeftOffset = dragEvent.mouseOffset.minus(group._grabOffset) ;

		// TODO: replace with DragThreshold object
		if (!group._isThresholdExceeded) {
			var distance = 
					dragEvent.mouseOffset.distance(group._initialMouseOffset) ;
			if (distance < group._thresholdDistance) return ;
			group._isThresholdExceeded = true ;
			group._notifyListeners(
		   group.factory._createEvent('dragstart', event, group)) ;
		}

		for (i in group._transforms) {
			var transform = group._transforms[i] ;
			newTopLeftOffset = transform(newTopLeftOffset, dragEvent) ;
		}
   if(!newTopLeftOffset) return ;
		var dragDelta = newTopLeftOffset.minus(dragEvent.topLeftOffset);
   if(!dragDelta) return ;
		var newTopLeftPosition = dragEvent.topLeftPosition.plus(dragDelta) ;
		newTopLeftPosition.reposition(group.element) ;      
		dragEvent.transformedMouseOffset = newTopLeftOffset.plus(group._grabOffset) ;

		group._notifyListeners(dragEvent) ;

		var errorDelta = newTopLeftOffset.minus(coordinates.topLeftOffset(group.element)) ;
      if(!errorDelta) return ;
		if (errorDelta.x != 0 || errorDelta.y != 0) {
			coordinates.topLeftPosition(group.element).plus(errorDelta).reposition(group.element);
		}
	},

	_dragEnd : function(event) {
		event = ToolMan.events().fix(event)
		var group = this.toolManDragGroup ;

//The flinn says: it may happen that group is NULL! (on ie) therefore next statement would break.
      if ( null == group || typeof group == 'undefined')  
      {  
         return ;
      }
		var dragEvent = group.factory._createEvent('dragend', event, group) ;

		this.toolManDragGroup = null ;
		ToolMan.events().unregister(document, 'mousemove', group._drag) ;
		document.onmousemove = null ;
		ToolMan.events().unregister(document, 'mouseup', group._dragEnd);
      group._notifyListeners(dragEvent) ;
	},

	_notifyListeners : function(dragEvent) {
		var listeners = this._listeners[dragEvent.type] ;

      if( dragEvent.mousePosition.x < 0 ) dragEvent.mousePosition.x = 0 ;
      //if( dragEvent.mousePosition.y < 0 ) dragEvent.mousePosition.y = 0 ;


		for (i in listeners) {          
			listeners[i](dragEvent) ;
		}
	}
}

function _ToolManDragEvent(type, event, group) {
	this.type = type
	this.group = group
	this.mousePosition = ToolMan.coordinates().mousePosition(event)
	this.mouseOffset = ToolMan.coordinates().mouseOffset(event)
	this.transformedMouseOffset = this.mouseOffset
	this.topLeftPosition = ToolMan.coordinates().topLeftPosition(group.element)
	this.topLeftOffset = ToolMan.coordinates().topLeftOffset(group.element)
}

_ToolManDragEvent.prototype = {
	toString : function() {
		return "mouse: " + this.mousePosition + this.mouseOffset + "    " +
				"xmouse: " + this.transformedMouseOffset + "    " +
				"left,top: " + this.topLeftPosition + this.topLeftOffset
	}
}

ToolMan._dragFactory._constraintFactory = {
	vertical : function() {
		return function(coordinate, dragEvent) {
			var x = dragEvent.topLeftOffset.x
			return coordinate.x != x
					? coordinate.factory.create(x, coordinate.y) 
					: coordinate
		}
	},

	horizontal : function() {
		return function(coordinate, dragEvent) {
			var y = dragEvent.topLeftOffset.y
			return coordinate.y != y
					? coordinate.factory.create(coordinate.x, y) 
					: coordinate
		}
	}
}
