//>>built
define("dojox/geo/charting/MouseInteractionSupport", ["dojo/_base/lang","dojo/_base/declare","dojo/_base/event",
		"dojo/_base/connect","dojo/_base/window","dojo/_base/html","dojo/dom","dojo/_base/sniff"],
  function(lang, declare, event, connect, win, html, dom, has) {

return declare("dojox.geo.charting.MouseInteractionSupport", null, {
	//	summary: 
	//		class to handle mouse interactions on a dojox.geo.charting.Map widget
	//	tags:
	//		private
	
	_map : null,
	_mapClickLocation : null,
	_screenClickLocation: null,
	_mouseDragListener: null,
	_mouseUpListener: null,
	_mouseUpClickListener: null,
	_mouseDownListener: null,
	_mouseMoveListener: null,
	_mouseWheelListener: null,
	_currentFeature: null,
	_cancelMouseClick: null,
	_zoomEnabled: false,
	_panEnabled: false,
	_onDragStartListener: null,
	_onSelectStartListener: null,


	mouseClickThreshold: 2,

	constructor : function(/* Map */map,/*boolean*/options) {
		//	summary: 
		//		Constructs a new _MouseInteractionSupport instance
		//	map: dojox.geo.charting.Map
		//		the Map widget this class provides touch navigation for.
		//	options: object
		//		to enable panning and mouse wheel zooming
		this._map = map;
		this._mapClickLocation = {x: 0,y: 0};
		this._screenClickLocation = {x: 0,y: 0};
		this._cancelMouseClick = false;
		if (options) {
			this._zoomEnabled = options.enableZoom;
			this._panEnabled = options.enablePan;
			if (options.mouseClickThreshold && options.mouseClickThreshold > 0) {
				this.mouseClickThreshold = options.mouseClickThreshold;
			}
		}
	},

	setEnableZoom: function(enable){
		//	summary: 
		//		enables mouse zoom on the map
		if (enable && !this._mouseWheelListener) {
			// enable
			var wheelEventName = !has("mozilla") ? "onmousewheel" : "DOMMouseScroll";
			this._mouseWheelListener = this._map.surface.connect(wheelEventName, this, this._mouseWheelHandler);
		} else if (!enable && this._mouseWheelListener) {
			// disable
			connect.disconnect(this._mouseWheelListener);
			this._mouseWheelListener = null;
		}
		this._zoomEnabled = enable;
	},

	setEnablePan: function(enable){
		//	summary: 
		//		enables mouse panning on the map
		this._panEnabled = enable;
	},

	connect: function() {
		//	summary: 
		//		connects this mouse support class to the Map component
		
		// install mouse listeners
		this._mouseMoveListener = this._map.surface.connect("onmousemove", this, this._mouseMoveHandler);
		this._mouseDownListener = this._map.surface.connect("onmousedown", this, this._mouseDownHandler);
		
		if (has("ie")) {
			_onDragStartListener = connect.connect(win.doc,"ondragstart",this,event.stop);
			_onSelectStartListener = connect.connect(win.doc,"onselectstart",this,event.stop);			
		}
		
		this.setEnableZoom(this._zoomEnabled);
		this.setEnablePan(this._panEnabled);
	},

	disconnect: function() {
		//	summary: 
		//		disconnects any installed listeners
		
		// store zoomPan state
		var isZoom = this._zoomEnabled;
		
		// disable zoom (disconnects listeners..)
		this.setEnableZoom(false);
		
		// restore value
		this._zoomEnabled = isZoom;
		
		// disconnect remaining listeners
		if (this._mouseMoveListener) {
			connect.disconnect(this._mouseMoveListener);
			this._mouseMoveListener = null;
			connect.disconnect(this._mouseDownListener);
			this._mouseDownListener = null;
		}
		if (this._onDragStartListener) {
			connect.disconnect(this._onDragStartListener);
			this._onDragStartListener = null;
			connect.disconnect(this._onSelectStartListener);
			this._onSelectStartListener = null;
		}
		
	},

	_mouseClickHandler: function(mouseEvent) {
		//	summary: 
		//		action performed on the map when a mouse click was performed
		//	mouseEvent: the mouse event
		//	tags:
		//		private
		
		var feature = this._getFeatureFromMouseEvent(mouseEvent);
		
		if (feature) {
			// call feature handler
			feature._onclickHandler(mouseEvent);
		} else {
			// unselect all
			for (var name in this._map.mapObj.features){
				this._map.mapObj.features[name].select(false);
			}
			this._map.onFeatureClick(null);
		}
			
	},

	_mouseDownHandler: function(mouseEvent){
		//	summary: 
		//		action performed on the map when a mouse down was performed
		//	mouseEvent: the mouse event
		//	tags:
		//		private
		
		
		event.stop(mouseEvent);
		
		this._map.focused = true;
		// set various status parameters
		this._cancelMouseClick = false;
		this._screenClickLocation.x =  mouseEvent.pageX;
		this._screenClickLocation.y =  mouseEvent.pageY;

		// store map location where mouse down occurred
		var containerBounds = this._map._getContainerBounds();
		var offX = mouseEvent.pageX	- containerBounds.x,
			offY = mouseEvent.pageY - containerBounds.y;
		var mapPoint = this._map.screenCoordsToMapCoords(offX,offY);
		this._mapClickLocation.x = mapPoint.x;
		this._mapClickLocation.y = mapPoint.y;

		// install drag listener if pan is enabled
		if (!has("ie")) {
			this._mouseDragListener = connect.connect(win.doc,"onmousemove",this,this._mouseDragHandler);
			this._mouseUpClickListener = this._map.surface.connect("onmouseup", this, this._mouseUpClickHandler);
			this._mouseUpListener = connect.connect(win.doc,"onmouseup",this, this._mouseUpHandler);
		} else {
			var node = dom.byId(this._map.container);
			this._mouseDragListener = connect.connect(node,"onmousemove",this,this._mouseDragHandler);
			this._mouseUpClickListener = this._map.surface.connect("onmouseup", this, this._mouseUpClickHandler);
			this._mouseUpListener = this._map.surface.connect("onmouseup", this, this._mouseUpHandler);
			this._map.surface.rawNode.setCapture();
		}
	},

	_mouseUpClickHandler: function(mouseEvent) {
		
		if (!this._cancelMouseClick) {
			// execute mouse click handler
			this._mouseClickHandler(mouseEvent);
		}
		this._cancelMouseClick = false;
		
	},

	_mouseUpHandler: function(mouseEvent) {
		//	summary: 
		//		action performed on the map when a mouse up was performed
		//	mouseEvent: the mouse event
		//	tags:
		//		private
		
		event.stop(mouseEvent);
		
		this._map.mapObj.marker._needTooltipRefresh = true;
		
		// disconnect listeners
		if (this._mouseDragListener) {
			connect.disconnect(this._mouseDragListener);
			this._mouseDragListener = null;
		}
		if (this._mouseUpClickListener) {
			connect.disconnect(this._mouseUpClickListener);
			this._mouseUpClickListener = null;
		}
		if (this._mouseUpListener) {
			connect.disconnect(this._mouseUpListener);
			this._mouseUpListener = null;
		}
		
		if (has("ie")) {
			this._map.surface.rawNode.releaseCapture();
		}
	},

	_getFeatureFromMouseEvent: function(mouseEvent) {
		//	summary: 
		//		utility function to return the feature located at this mouse event location
		//	mouseEvent: the mouse event
		//	returns: dojox.geo.charting.Feature
		//		the feature found if any, null otherwise.
		//	tags:
		//		private
		var feature = null;
		if (mouseEvent.gfxTarget && mouseEvent.gfxTarget.getParent) {
			feature = this._map.mapObj.features[mouseEvent.gfxTarget.getParent().id];
		}
		return feature;
	},

	_mouseMoveHandler: function(mouseEvent) {
		//	summary: 
		//		action performed on the map when a mouse move was performed
		//	mouseEvent: the mouse event
		//	tags:
		//		private

		
		// do nothing more if dragging
		if (this._mouseDragListener && this._panEnabled) {
			return;
		}
		
		// hover and highlight
		var feature = this._getFeatureFromMouseEvent(mouseEvent);

		// set/unset highlight
		if (feature != this._currentFeature) {
			if (this._currentFeature) {
				// mouse leaving component
				this._currentFeature._onmouseoutHandler();
			}
			this._currentFeature = feature;
			
			if (feature)
				feature._onmouseoverHandler();
		}

		if (feature)
			feature._onmousemoveHandler(mouseEvent);
	},

	_mouseDragHandler: function(mouseEvent){
		//	summary: 
		//		action performed on the map when a mouse drag was performed
		//	mouseEvent: the mouse event
		//	tags:
		//		private
		
		// prevent browser interaction
		event.stop(mouseEvent);
		
		
		// find out if this the movement discards the "mouse click" gesture
		var dx = Math.abs(mouseEvent.pageX - this._screenClickLocation.x);
		var dy = Math.abs(mouseEvent.pageY - this._screenClickLocation.y);
		if (!this._cancelMouseClick && (dx > this.mouseClickThreshold || dy > this.mouseClickThreshold)) {
			// cancel mouse click
			this._cancelMouseClick = true;
			if (this._panEnabled) {
				this._map.mapObj.marker.hide();
			}
		}
		
		if (!this._panEnabled)
			return;

		var cBounds = this._map._getContainerBounds();
		var offX = mouseEvent.pageX - cBounds.x,
		offY = mouseEvent.pageY - cBounds.y;
		
		// compute map offset
		var mapPoint = this._map.screenCoordsToMapCoords(offX,offY);
		var mapOffsetX = mapPoint.x - this._mapClickLocation.x;
		var mapOffsetY = mapPoint.y - this._mapClickLocation.y;

		// adjust map center
		var currentMapCenter = this._map.getMapCenter();
		this._map.setMapCenter(currentMapCenter.x - mapOffsetX, currentMapCenter.y - mapOffsetY);
		
	},

	_mouseWheelHandler: function(mouseEvent) {
		//	summary: 
		//		action performed on the map when a mouse wheel up/down was performed
		//	mouseEvent: the mouse event
		//	tags:
		//		private
		

		// prevent browser interaction
		event.stop(mouseEvent);
		
		// hide tooltip
		this._map.mapObj.marker.hide();
		
		// event coords within component
		var containerBounds = this._map._getContainerBounds();
		var offX = mouseEvent.pageX - containerBounds.x,
			offY = mouseEvent.pageY - containerBounds.y;
		
		// current map point before zooming
		var invariantMapPoint = this._map.screenCoordsToMapCoords(offX,offY);

		// zoom increment power
		var power  = mouseEvent[(has("mozilla") ? "detail" : "wheelDelta")] / (has("mozilla") ? - 3 : 120) ;
		var scaleFactor = Math.pow(1.2,power);
		this._map.setMapScaleAt(this._map.getMapScale()*scaleFactor ,invariantMapPoint.x,invariantMapPoint.y,false);
		this._map.mapObj.marker._needTooltipRefresh = true;
		
	}
});
});
