﻿TRAILS.utils.GMapPedometer = function(mapElement){
	if(TRAILS.utils.GMapPedometer.instance)
		return TRAILS.utils.GMapPedometer.instance;

	var self = TRAILS.utils.GMapPedometer.instance = this;
	var waypoints = new Array();
	var endMarker = null;
	var startMarker = null;
	var querystring = new TRAILS.utils.QuerystringParser();
	var map = new google.maps.Map2(mapElement);
	var mapUI = map.getDefaultUI();

	function convertRadiansToMiles(n){
		return n / 1609.344;
	}
	function convertRadiansToKilometers(n){
		return n / 1000;
	}
	function convertDegreesToRadians(n){
		return n * (Math.PI / 180.0);
	}
	function getWaypointsDistance(waypoint1, waypoint2){
		var waypoint1Rad = convertDegreesToRadians(90 - waypoint1.y);
		var waypoint2Rad = convertDegreesToRadians(90 - waypoint2.y);

		return Math.acos(Math.cos(waypoint1Rad) * Math.cos(waypoint2Rad) + Math.sin(waypoint1Rad) * Math.sin(waypoint2Rad) * Math.cos(convertDegreesToRadians(waypoint1.x - waypoint2.x))) * 6367000; 
	}
	function getWaypointsDistanceTotal(){
		var distance = 0;
		
		if (waypoints.length < 2)
			return distance;

		for (var i = 0, l = waypoints.length - 1; i < l; i++)
			distance += getWaypointsDistance(waypoints[i], waypoints[i + 1]);

		return distance;
	}
	function drawWaypoints(){
		if(waypoints.length == 0) return;

		map.addOverlay(new google.maps.Marker(waypoints[0]));
		
		if(waypoints.length == 1) return;
			
		map.addOverlay(new google.maps.Marker(waypoints[waypoints.length - 1]));
		map.addOverlay(new google.maps.Polyline(waypoints));
	}
	
	self.Geocoder = new google.maps.ClientGeocoder();
	self.distanceFormatId = {
		MILES: 'miles',
		KILOMETERS: 'kilometers'
	};
	self.getDistance = function(distanceFormatId){
		switch(distanceFormatId){
			case self.distanceFormatId.MILES:
				return convertRadiansToMiles(getWaypointsDistanceTotal());
			case self.distanceFormatId.KILOMETERS:
				return convertRadiansToKilometers(getWaypointsDistanceTotal());
		}
		
		return getWaypointsDistanceTotal();
	};
	self.addListener = function(event, fn){
		google.maps.Event.addListener(map, event, fn);
		
		return self;
	};
	self.removeWaypoint = function(n){		
		if(waypoints[n])
			waypoints.splice(n, 1);
		else
			waypoints = new Array();
			
		map.clearOverlays();
		drawWaypoints();
		
		return self;
	};
	self.getWaypointCount = function(){
		return waypoints.length;
	};
	self.setCenter = function(latitude, longitude, zoom){
		if(typeof zoom === Number.type)
			map.setCenter(new google.maps.LatLng(latitude, longitude), zoom);
		else
			self.Geocoder.getLocations(new google.maps.LatLng(latitude, longitude), function(locations){
				self.setCenter(locations.Placemark[0].Point.coordinates[1], locations.Placemark[0].Point.coordinates[0], Math.round(locations.Placemark[0].AddressDetails.Accuracy * 1.4));
			});
		
		return self;
	};
	self.getCenter = function(){
		return map.getCenter();
	};
	self.getZoom = function(){
		return map.getZoom();
	};
	self.getEncodedWaypoints = function(){
		var decodedWaypoints = new Array();
		var encodedWaypoints = new Array();
		var x0 = 0;
		var y0 = 0;
		
		function fn(f){
			while (true){
				var e = f & 31;
				
				f >>= 5;
				
				if(f) e |= 32;
				
				encodedWaypoints.push(String.fromCharCode(e + 63));
				
				if(f == 0) break;
			}
		}
		
		for(var i = 0, l = waypoints.length; i < l; i++){
			decodedWaypoints.push(Math.round(waypoints[i].y / 1.0E-5));
			decodedWaypoints.push(Math.round(waypoints[i].x / 1.0E-5));
		}
		for(var i = 0, l = decodedWaypoints.length / 2; i < l; i++){
			var y = decodedWaypoints[i << 1];
			var dy = y - y0;				
			var f = (Math.abs(dy) << 1) - (dy < 0);
			
			y0 = y;

			fn(f);

			var x = decodedWaypoints[(i << 1) + 1]
			var dx = x - x0;
			
			x0 = x;
			f = (Math.abs(dx) << 1) - (dx < 0);
			
			fn(f);
		}

		return encodedWaypoints.join(String.empty);
	};
	self.getDecodedWaypoints = function(encodedWaypoints){
		var decodedWaypoints = new Array();
		var count = 0;
		var x = 0;
		var y = 0;
		
		function decodeCoordinate(f){
			var oc = 0;
			var Fa = 0;

			while(true){
				var ub = encodedWaypoints.charCodeAt(count) - 63;
				
				count++;
				Fa |= (ub & 31) << oc;
				oc += 5;
				
				if(ub < 32) break;
			}
			
			return f += Fa & 1 ? ~(Fa >> 1) : Fa >> 1;
		}

		while(count < encodedWaypoints.length){
			x = decodeCoordinate(x);
			y = decodeCoordinate(y);
			
			decodedWaypoints.push(new google.maps.Point(y * 1.0E-5, x * 1.0E-5))
		}
		
		return decodedWaypoints;
	};
	
	mapUI.zoom.doubleclick = false;

	jQuery(window).unload(function(){ google.maps.Unload(); });
	map.setMapType(google.maps.PHYSICAL_MAP);
	map.setUI(mapUI);
	/*
	map.addMapType(google.maps.SATELLITE_3D_MAP);
	map.getEarthInstance(function(earth){
		earth.getLayerRoot().enableLayerById(earth.LAYER_BORDERS, true);
	});
	*/
	self.addListener('click', function(overlay, waypoint) {
		if(!waypoint) return;
		
		waypoints.push(waypoint);
		map.clearOverlays();
		drawWaypoints();
	});
	if(querystring.contains('path') &&
			(function(x, y, z, zl, path){
				if(!path)
					return false;
				
				waypoints = path;
				
				drawWaypoints();
				
				if(isNaN(x) || isNaN(y))
					return false;
													
				map.setCenter(new google.maps.LatLng(y, x), isNaN(z) ? isNaN(zl) ? 4 : 17 - zl : z);
				
				return true;
			})(parseFloat(querystring.getField('x')), parseFloat(querystring.getField('y')), parseInt(querystring.getField('z'), 10), parseInt(querystring.getField('zl'), 10), self.getDecodedWaypoints(querystring.getField('path'))))
		return true;
	
	if(google.loader && google.loader.ClientLocation)
		self.setCenter(google.loader.ClientLocation.latitude, google.loader.ClientLocation.longitude);
	else
		self.setCenter(40.441944, -100.141944, 4);
};