/**
	TouchMap class
	IMPORTANT: currently only supports Lat/Lon projection!
	Author: Yaniv Zimet
	Copyright 2008 Weather Underground, Inc.
*/

function TouchMap(options) {
	this.options = options || {};
	
	var log_id = options.log || "log";
	var wrapper_id = options.wrapper || "wu_wrapper";
	var container_id = options.container || "wu_container";
	var map_id = options.map || "wu_map";
	var eventcapture_id = options.eventcapture || "wu_eventcapture";
	var centerTarget_id = options.centerTarget || "wu_centerTarget";
	var linkToConditions_id = options.linkToConditions || "linkToConditions";
	
	this.minWidth = options.minWidth || 200;
	this.minHeight = options.minHeight || 200;
	
	this.clat = options.lat || 38.410558;
	this.clon = options.lon || -96.855469;
	this.radius = options.radius || 1000;
	
	this.minRadius = options.minRadius || 10;
	this.maxRadius = options.maxRadius || 2000;
	this.zoomScaleFactor = options.zoomScaleFactor || 2.0; // must be greater than 1
	this.zoomWheelScale = options.zoomWheelScale || 1.1; // must be greater than 1
	this.zoomWheelDelay = options.zoomWheelDelay || 200; // milliseconds
	
	this.getURLcustom = options.getURL || null;
	this.getURLtoConditions = options.getURLtoConditions || null;
	this.doubleTapDelay = options.doubleTapDelay || 1000; // milliseconds
	this.overlays = options.overlays || {}; // object of img_id : getURLfunction
	
	// will load extra around the image for pretty dragging (must br greater than 1 to activate)
	this.extraLoadFactor = options.extraLoadFactor || 1.0;
	this.loadExtra = false;
	this.topInit = 0;
	this.leftInit = 0;
	// YAZ 20090403 - allow adding pixels to be able to scroll past URL bar in iPhone
	this.extraHeight = options.extraHeight || 0;
	// YAZ 20090406 - to show note when request is in progress
	this.stillWorking = $(options.stillWorking) || null;
	
	this.widthToStrech = 600;
	this.aspectRatio = 3.0/2.0;
	
	this.width = this.minWidth;
	this.height = this.minHeight;
	this.latspan = 0.0;
	this.lonspan = 0.0
	
	this.loading = true;
	this.drag = null; // the draggable object instance
	
	this.log = $(log_id);
	this.wrapper = $(wrapper_id);
	this.container = $(container_id);
	this.map = $(map_id);
	this.eventcapture = $(eventcapture_id);
	this.centerTarget = $(centerTarget_id);
	this.linkToConditions = $(linkToConditions_id);
	
	this.initialize();
	this.iPhone();
}

TouchMap.prototype.initialize = function() {	
	var originalWidth = this.width;
	var originalHeight = this.height;
	var lonspan = 79.013671875;
	var latspan = 41.55544956559;
	// this.dx = lonspan / originalWidth;
	// this.dy = latspan / originalHeight;
	
	this.map.addEvents({
		"load": function() {
			this.resizeMap();
			this.setLoading(false);
			
			// should we load again with a larger image?
			if (this.extraLoadFactor > 1.0) {
				if (this.loadExtra == true) {
					// got here after loading a loadExtra, so set back to false
					this.loadExtra = false;
					
				} else {
					// load a large image
					this.loadExtra = true;
					this.updateMap();
				}
			}
		}.bindWithEvent(this),
		"error": function() {
			alert('Error: failed to load map image. Please try again.');
			this.setLoading(false);
		}.bindWithEvent(this),
		"abort": function() {
			alert('Error: aborted loading map image. Please try again.');
			this.setLoading(false);
		}.bindWithEvent(this)
	});

	this.stretchMap();
//	this.addControls();
	this.enableWheel();
	this.enableDrag();
	this.enableDoubleClickZoom();
	this.enableResizeRefresh();
	if (this.linkToConditions) {
		this.enableGotoLocation();
	}
}

/**
	iPhone
	Thanks to: http://www.sitepen.com/blog/2008/07/10/touching-and-gesturing-on-the-iphone/
*/
TouchMap.prototype.iPhone = function() {	
	var i = 0;
	var j = 0;
	var log = this.log;
	var dragging = false;
	var activeDrag = false;
	var sizing = false;
	var scale = 1;
	var newScale = 1;
	var me = this;
	var minRadius = this.minRadius;
	var maxRadius = this.maxRadius;
	var taps = 0;
	var resetTapsTimer = null;
	var doubleTapDelay = this.doubleTapDelay;
	//var elt = this.map;
	var elt = this.eventcapture; // the element that will change position/size (not necessarily the topmost element that was dragged)
	
	function touchstart(e) {
		e.preventDefault(); // disable "save image" dialog box
		// YAZ 20080406 - show note while still loading an image
		if (me.loading) {
			me.setStillWorking(true);
			return;
		}
		//var node = $(e.changedTouches[0].target);
		var left = e.targetTouches[0].pageX - elt.getStyle('left').toFloat();
		var top = e.targetTouches[0].pageY - elt.getStyle('top').toFloat();
		dragging = [left, top];
		taps++;
		if (log) log.setHTML('touchstart');
	}
	
	function touchmove(e) {
		if (dragging && !sizing) {
			// to prevent simultaneous sizing
			activeDrag = true;
			// only handle 1-finger drags
			if (e.touches.length == 1) {
				e.preventDefault();
				//var node = $(e.changedTouches[0].target);
				var left = e.changedTouches[0].pageX - dragging[0];
				var top = e.changedTouches[0].pageY - dragging[1];
				elt.setStyle('left', left+'px');
				elt.setStyle('top', top+'px');
			}
			if (log) log.setHTML('touchmove, length: ' +e.touches.length);
		}
		taps = 0;
	}
	
	function touchend(e) {
		if (dragging && !sizing) {
			// e.preventDefault();
			//var node = $(e.changedTouches[0].target);
			var top = elt.getStyle('top').toFloat() - me.topInit;
			var left = elt.getStyle('left').toFloat() - me.leftInit;
			me.recenterMap(top, left);
			if (log) log.setHTML('touchend, length: ' +e.touches.length+ ', taps:' +taps);
		}
		dragging = false;
		activeDrag = false;
		
		checkDoubleTap();
		
		// var html = "";
		// for (var o in e.touches) {
		// 	html += o + " ::: " + e[o] + "<br/>";
		// }
		// if (log) log.setHTML(html);
	}
	
	function gesturestart(e) {
		taps = 0;
		if (!activeDrag) {
			e.preventDefault(); // disable "save image" dialog box
			// YAZ 20080406 - show note while still loading an image
			if (me.loading) {
				me.setStillWorking(true);
				return;
			}
			var node = $(e.target);
			var width = node.getStyle('width').toFloat();
			var height = node.getStyle('height').toFloat();
			sizing = [width, height];
		}
	}
	
	function gesturechange(e) {
		if (sizing && !activeDrag) {
			i++;
			var node = $(e.target);
			// can't zoom too much
			var newRadius = me.radius / (scale * e.scale);
			if (newRadius >= minRadius && newRadius <= maxRadius) {
				newScale = scale * e.scale;
				elt.setStyles({
					"webkitTransform": "scale(" + newScale + ")"
				});
			}

			html = "gesturechange i=" +i + " j=" +j;
			html += " newRadius=" +newRadius;
			if (log) log.setHTML(html);
		}
	}
	
	function gestureend(e) {
		if (sizing && !activeDrag) {
			j++;
			var node = $(e.target);
			elt.setStyles({
				"webkitTransform": null
			});
			me.zoom(newScale, 0);

			sizing = false;
			dragging = false; // to prevent a drag when the 1st finger lifts
			scale = (1);
			html = "gestureend i=" +i + " j=" +j;
			if (log) log.setHTML(html);
		}
	}
	
	function resetTaps() {
		taps = 0;
	}
	
	function checkDoubleTap() {
		if (resetTapsTimer) $clear(resetTapsTimer);
		if (taps >= 2) {
			doubleTap();
			resetTaps();
		} else {
			resetTapsTimer = resetTaps.delay(doubleTapDelay);
		}
	}
	
	function doubleTap() {
		me.zoomIn();
	}
	
	// add the event listeners
	if (document.addEventListener) {
		elt.addEventListener("touchstart", touchstart, false);
		elt.addEventListener("touchmove", touchmove, false);
		elt.addEventListener("touchend", touchend, false);
		elt.addEventListener("gesturestart", gesturestart, false);
		elt.addEventListener("gesturechange", gesturechange, false);
		elt.addEventListener("gestureend", gestureend, false);
	}
}

TouchMap.prototype.addControls = function() {
	var controls = new Element('div', {
		'class': 'controls left'
	});
	
	var zoomIn = new Element('a', {
		'href': '#',
		'class': 'control',
		'events': {
			'click': function(e) {
				e = new Event(e).stop();
				this.zoomIn();
			}.bindAsEventListener(this)
		}
	}).setHTML('+');
	
	var zoomOut = new Element('a', {
		'href': '#',
		'class': 'control',
		'events': {
			'click': function(e) {
				e = new Event(e).stop();
				this.zoomOut();
			}.bindAsEventListener(this)
		}
	}).setHTML('&ndash;');
	
	controls.adopt(zoomIn).adopt(zoomOut);
	controls.injectInside(this.wrapper);
}

TouchMap.prototype.enableWheel = function() {
	this.eventcapture.addEvent('mousewheel', function(event) {
		event = new Event(event).stop();
		
		if (event.wheel > 0) {
			this.zoom(this.zoomWheelScale, this.zoomWheelDelay);
		}
		else if (event.wheel < 0) {
			this.zoom(1.0 / this.zoomWheelScale, this.zoomWheelDelay);
		}
		
		var html = "mouse wheel: " +event.wheel;
		if (this.log) this.log.setHTML(html);
	}.bindWithEvent(this));
}

TouchMap.prototype.enableDoubleClickZoom = function() {
	this.eventcapture.addEvent('dblclick', function(event) {
		this.zoomIn();
	}.bindWithEvent(this));
}

TouchMap.prototype.zoomIn = function() {
	this.zoom(this.zoomScaleFactor, 0);
}

TouchMap.prototype.zoomOut = function() {
	this.zoom(1.0 / this.zoomScaleFactor, 0);
}

TouchMap.prototype.zoom = function(scale, delay) {
	// is the map still loading
	if (this.loading) return;
	
	// are we zooming in or out
	var isZoomIn;
	if (scale > 1) {isZoomIn = true;}
	if (scale < 1) {isZoomIn = false;}
	if (scale == 1) {return;}
	
	// can't zoom too much
	var newRadius = this.radius / scale;
	if (newRadius < this.minRadius || newRadius > this.maxRadius) return;
	
	var extra = scale - 1.0;
	if (!isZoomIn) {
		extra = -(1.0 - scale);
	}

	// var elt = this.map;
	var elt = this.eventcapture;
	
	var top = elt.getStyle('top').toFloat();
	var left = elt.getStyle('left').toFloat();
	
	var width = elt.getStyle('width').toFloat();
	var height = elt.getStyle('height').toFloat();

	var topExtra = height * extra / 2.0;
	var leftExtra = width * extra / 2.0;
	
	top -= topExtra;
	left -= leftExtra;

	width *= scale;
	height *= scale;

	elt.setStyle('width', width+'px');
	elt.setStyle('height', height+'px');

	elt.setStyle('top', top+'px');
	elt.setStyle('left', left+'px');
	
	// this.dx /= scale;
	// this.dy /= scale;
	this.radius /= scale;
	
	var html = "scale: " +scale+ ", radius: " +this.radius;
	if (this.log) this.log.setHTML(html);
	
	if (delay) {
		this.zoomEnd(delay);
	} else {
		this.zoomEnd();
	}
}

TouchMap.prototype.zoomEnd = function(delay) {
	if (delay) {
		this.updateMap(delay);
	} else {
		this.updateMap();
	}
}

TouchMap.prototype.enableDrag = function() {
	var elt = this.eventcapture;
	this.drag = new Drag.Base(elt, {
		// 'handle': this.container,
		// 'limit' : {'x': [-200, 200],'y': [-100, 100]},
		'onComplete': function() {
			var top = elt.getStyle('top').toFloat() - this.topInit;
			var left = elt.getStyle('left').toFloat() - this.leftInit;
			this.recenterMap(top, left);
		}.bind(this)
	});
	// disable because it will be enabled when the map loads
	this.drag.detach();
}

TouchMap.prototype.enableResizeRefresh = function() {
	this.windowWidth = window.getWidth();
	this.windowHeight = window.getHeight();
	
	window.addEvent("resize", function() {
		if (this.resizeTimer) $clear(this.resizeTimer);
		this.resizeTimer = this.handleRefresh.delay(250, this);
	}.bind(this));
}

// only if the size of the window actually changed
TouchMap.prototype.handleRefresh = function() {
	var width = window.getWidth();
	var height = window.getHeight();
	
	if ((this.windowWidth != width) || (this.windowHeight != height)) {
		this.windowWidth = width;
		this.windowHeight = height;
		
		this.stretchMap();
	}
}

TouchMap.prototype.stretchMap = function() {
	// var size = this.container.getSize();
	var size = this.wrapper.getSize();
	
	var width = size.size.x;
	// YAZ 20090403 - allow adding pixels to be able to scroll past URL bar in iPhone
	var height = size.size.y + this.extraHeight;
	
	if (width < this.minWidth) width = this.minWidth;
	if (height < this.minHeight) height = this.minHeight;
	
	//if (width >= this.widthToStrech) {
	//	height = width / this.aspectRatio;
	//} else {
	//	height = this.minHeight;
	//}
	
	// set the container size (IE doesn't do this by default)
	this.container.setStyle('width', width + 'px');
	this.container.setStyle('height', height + 'px');
	
	// set the wrapper height (because it contains an absolutely positioned div)
	// YAZ 20090324 - has CSS height 100% instead
	//this.wrapper.setStyle('height', height + 'px');
	
	// set the map size
	this.width = width;
	this.height = height;
	
	if (this.log) this.updateLog();
	this.updateMap();
	
	// make sure the center target is centered
	if (this.centerTarget) {
		this.centerTarget.removeClass('none'); // in order for the size value to exist
		var targetSize = this.centerTarget.getSize();
		this.centerTarget.addClass('none'); // we got the size value, now return to display none
		var targetWidth = targetSize.size.x;
		var targetHeight = targetSize.size.y;
		var targetTop = (height - targetHeight) / 2;
		var targetLeft = (width - targetWidth) / 2;
		this.centerTarget.setStyle('top', targetTop + 'px');
		this.centerTarget.setStyle('left', targetLeft + 'px');
	}

	// this.map.setStyle('webkitTransform', 'scale(1.5)');
}

TouchMap.prototype.resizeMap = function() {
	var top = 0;
	var left = 0;
	var width = this.width;
	var height = this.height;
	
	if (this.loadExtra == true) {
		width *= this.extraLoadFactor;
		height *= this.extraLoadFactor;
		left = (width - this.width) / -2.0;
		top = (height - this.height) / -2.0;
	}

	// var elt = this.map;
	var elt = this.eventcapture;
	
	this.topInit = top;
	this.leftInit = left;
	elt.setStyles({
		'top': top + 'px',
		'left': left + 'px',
		'width': width + 'px',
		'height': height + 'px'
		//'height': (height + 60) + 'px'
	});
}

TouchMap.prototype.updateLog = function() {
	this.log.empty();
	var html = "";
	html += "this (map) width:" +this.width+ ", height:" +this.height+ "<br/>";
	
	this.log.setHTML(html);
}

TouchMap.prototype.recenterMap = function(top, left) {
	if (this.extraLoadFactor > 1.0) {
		top /= this.extraLoadFactor;
		left /= this.extraLoadFactor;
	}
	var lon = left * (this.lonspan / this.width);
	var lat = top * (this.latspan / this.height);
	if (lat || lon) {
		this.clon -= lon;
		this.clat += lat;
		this.updateMap();
	}
}

TouchMap.prototype.updateMap = function(delay) {
	if (delay) {
		if (this.requestTimer) $clear(this.requestTimer);
		this.requestTimer = this.updateMapStart.delay(delay, this);
	} else {
		this.updateMapStart();
	}
}

TouchMap.prototype.refresh = function() {
	this.updateMap();
}

TouchMap.prototype.updateMapStart = function() {
	var width = this.width;
	var height = this.height;
	
	// radius in nautical miles
	var radius_lat = this.radius * (1/60.0);
	//var radius_lon = this.radius * (1/60.0);
	var radius_lon = this.radius * (1/60.0) / Math.cos(this.clat * Math.PI / 180.0);
	if (width > height) {
		radius_lon *= (width/height);
	}
	if (height > width) {
		radius_lat *= (height/width);
	}
	
	if (this.loadExtra == true) {
		radius_lat *= this.extraLoadFactor;
		radius_lon *= this.extraLoadFactor;
		width *= this.extraLoadFactor;
		height *= this.extraLoadFactor;
	}
	
	var maxlat = this.clat + radius_lat;
	var minlat = this.clat - radius_lat;
	var maxlon = this.clon + radius_lon;
	var minlon = this.clon - radius_lon;
	
	if (minlon < -180.0) minlon += 360.0;
	if (minlon > 180.0) minlon -= 360.0;
	if (maxlon < -180.0) maxlon += 360.0;
	if (maxlon > 180.0) maxlon -= 360.0;
	
	if (minlon > maxlon) minlon -= 360.0;

	if (this.clon < -180.0) this.clon += 360.0;
	if (this.clon > 180.0) this.clon -= 360.0

	this.latspan = maxlat - minlat;
	this.lonspan = maxlon - minlon;
		
	var url = this.getURL(width, height, maxlat, maxlon, minlat, minlon);
	
	this.setLoading(true);
	this.map.setProperty("src", url);
	// if (this.log) this.log.setHTML(url);
	this.map.fireEvent('moveend', [this.clat, this.clon, this.radius]);

	// YAZ 20090402 - additional overlays
	for (var overlay_id in this.overlays) {
		var overlay = $(overlay_id);
		var getURLfn = this.overlays[overlay_id];
		var overlay_url = getURLfn(width, height, maxlat, maxlon, minlat, minlon);

		if (overlay) {
			if (overlay_url && overlay_url.length > 0) {
				overlay.setProperty("src", overlay_url);
				overlay.removeClass('none');
			} else {
				overlay.addClass('none');
				overlay.setProperty("src", "");
			}
		}
	}
}

TouchMap.prototype.getURL = function(width, height, maxlat, maxlon, minlat, minlon) {
	if (this.getURLcustom) {
		return this.getURLcustom(width, height, maxlat, maxlon, minlat, minlon);
	}
	var url = "http://wublast.wunderground.com/cgi-bin/WUBLAST?maxlat="+maxlat+"&maxlon="+maxlon+"&minlat="+minlat+"&minlon="+minlon+"&width="+width+"&height="+height+"&gtt=109&num=1&delay=25&key=sat_ir4&proj=ll&basemap=1";
	return url;
}

TouchMap.prototype.setLoading = function(loading) {
	if (loading) {
		this.loading = true;
		if (this.drag) this.drag.detach();
		if (this.log) this.log.addClass('loading');
	}
	else {
		this.loading = false;
		if (this.log) this.log.removeClass('loading');
		if (this.drag) this.drag.attach();
		this.setStillWorking(false);
	}
}

TouchMap.prototype.setStillWorking = function(working) {
	if (working) {
		if (this.stillWorking) this.stillWorking.removeClass('none');
	}
	else {
		if (this.stillWorking) this.stillWorking.addClass('none');
	}
}

TouchMap.prototype.enableGotoLocation = function() {
	var linkToConditions = this.linkToConditions;
	var getURLtoConditions = this.getURLtoConditions;
	
	// show/hide the centerTarget image on hover
	var centerTarget = this.centerTarget;
	if (centerTarget) {
		linkToConditions.addEvents({
			'mouseenter': function() {
				centerTarget.removeClass('none');
			},
			'mouseleave': function() {
				centerTarget.addClass('none');
			}
		});
	}

	function setLinkToConditions(clat, clon, radius) {
		var url = "";
		if (getURLtoConditions) {
			url = getURLtoConditions(clat, clon, radius);
		} else {
			url = '/cgi-bin/findweather/getForecast?query=' +clat.toFixed(4)+ ',' +clon.toFixed(4)+ '#conditions';
		}
		linkToConditions.setProperty('href', url);
	}
	this.map.addEvent('moveend', setLinkToConditions);
	setLinkToConditions(this.clat, this.clon, this.radius);
}

