/* Copyright (c) 2005 Tim Taylor Consulting (see LICENSE.txt) */

ToolMan._dragFactory = {
	createSimpleGroup : function(element, handle) {
		handle = handle ? handle : element
		var group = this.createGroup(element)
		group.setHandle(handle)
		group.transparentDrag()
		group.onTopWhileDragging()
		return group
	},

	createGroup : function(element) {
		var group = new _ToolManDragGroup(this, element)

		var position = ToolMan.css().readStyle(element, 'position')
		if (position == 'static') {
		    if (BrowserDetect.browser != 'Explorer') //wade hacked this one in to allow for scrolling drag/drop sortable lists in IE
			    element.style["position"] = 'relative'
		} else if (position == 'absolute') {
			/* for Safari 1.2 */
			ToolMan.coordinates().topLeftOffset(element).reposition(element)
		}

		// TODO: only if ToolMan.isDebugging()
		group.register('draginit', this._showDragEventStatus)
		group.register('dragmove', this._showDragEventStatus)
		group.register('dragend', this._showDragEventStatus)

		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)
	}
}

function _ToolManDragGroup(factory, element) {
	this.factory = factory
	this.element = element
	this._handle = null
	this._thresholdDistance = 0
	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 != 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) {
        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) {
        var zIndex = typeof (zIndex) != "undefined" ? zIndex : 100000;
        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) {
            if (BrowserDetect.browser == "Explorer" && BrowserDetect.version > 7)
                originalZIndex = 0;
            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)
        }

        var dragDelta = newTopLeftOffset.minus(dragEvent.topLeftOffset)
        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.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
        var dragEvent = group.factory._createEvent('dragend', event, group)

        group._notifyListeners(dragEvent)

        this.toolManDragGroup = null
        ToolMan.events().unregister(document, 'mousemove', group._drag)
        document.onmousemove = null
        ToolMan.events().unregister(document, 'mouseup', group._dragEnd)
    },

    _notifyListeners: function(dragEvent) {
        var listeners = this._listeners[dragEvent.type]
        for (i in listeners) {
            listeners[i](dragEvent)
        } /*,
		
	//wade added this to account for if the UL is scrollable	
    _adjustScroll : function(dragEvent) {
        var xmouse = dragEvent.transformedMouseOffset
        var scrollOffset = ToolMan.coordinates()
        scrollOffset.x = dragEvent.group.element.parentNode.scrollLeft;
        scrollOffset.y = dragEvent.group.element.parentNode.scrollTop;
        xmouse = xmouse.plus(scrollOffset);
        
        //wade added this to auto-scroll the set lists of they are long and the user is dragging to a position not currently in view
        //uses Coord object and some functions from sitewide.js
        //this part only scrolls the window
        var scrollIncrement = 10;
        var listPosition = getObjectPositionRaw(dragEvent.group.element.parentNode);
        listPosition.width = trimpx(getElementStyleRaw(dragEvent.group.element.parentNode,"width"));
        listPosition.height = trimpx(getElementStyleRaw(dragEvent.group.element.parentNode,"height"));
        var listItemPosition = getObjectPositionRaw(dragEvent.group.element);
        listItemPosition.width = trimpx(getElementStyleRaw(dragEvent.group.element,"width"));
        listItemPosition.height = trimpx(getElementStyleRaw(dragEvent.group.element,"height"));
        //document.getElementById("short").getElementsByTagName("p")[0].innerHTML = "List:&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; (" + listPosition.toString() + ")<br/>List Item: (" + listItemPosition.toString() + ")<br/>Mouse:&#160;&#160;&#160; (" + xmouse.x + "," + xmouse.y + ")<br/>Scroll Up: (" + (xmouse.y - scrollOffset.y) + "," + (listPosition.y) + ") " + (xmouse.y - scrollOffset.y <= listPosition.y) + "<br/>Scroll Down: (" + (xmouse.y - scrollOffset.y) + "," + (listPosition.y + listPosition.height) + ") " + (xmouse.y - scrollOffset.y >= listPosition.y + listPosition.height);
        if (xmouse.y - scrollOffset.y <= listPosition.y && scrollOffset.y > 0) {
            if (scrollOffset.y - scrollIncrement > 0)
                scrollOffset.y -= scrollIncrement;
            else
                scrollOffset.y = 0;
            dragEvent.group.element.parentNode.scrollTop = scrollOffset.y
            }
        if (xmouse.y - scrollOffset.y >= listPosition.y + listPosition.height && scrollOffset.y < dragEvent.group.element.parentNode.scrollHeight) {
            if (scrollOffset.y + scrollIncrement < dragEvent.group.element.parentNode.scrollHeight)
                scrollOffset.y += scrollIncrement;
            else 
                scrollOffset.y = dragEvent.group.element.parentNode.scrollHeight;
            dragEvent.group.element.parentNode.scrollTop = scrollOffset.y
            }
        }*/
    }
}

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
		}
	}
}

