var trekpress = {
	
	units: { miles:0, kilometers:1, feet:2, meters:3 },
	iconUrlBase: 'http://maps.google.com/mapfiles/',
	map: null,
	map_bounds: null,
	infoWindow: null,
	chart: null,
	showWaypoints: true,
	showTracks: true,
	showImages: false,
	showMetric: false,
	
	createControlButton: function(title, selected) {
		if (selected == null) {
			selected = false;
		}
		var btn = document.createElement('span');
		btn.style.paddingLeft = '5px';
		btn.style.paddingRight = '5px';
		btn.style.verticalAlign = 'top';
		btn.style.color = "#000000";
		
		var chk = document.createElement('input');
		chk.type = 'checkbox';
		chk.name = 'overlay';
		chk.value = title;
		chk.checked = selected;
		google.maps.event.addDomListener(chk, 'click', function() {
			trekpress.toggleOverlayVisibility(title);
		});

		var txt = document.createTextNode(title);
		
		btn.appendChild(chk);
		btn.appendChild(txt);
		 
		return btn;
	},
	
	createOverlayVisibilityControl: function() {
		var control = document.createElement('div');
		control.style.margin = '5px';
		control.style.backgroundColor = 'white';
		control.style.borderColor = '#000000';
		control.style.borderStyle = 'solid';
		control.style.borderWidth = '1px';
		control.style.fontSize = "12px";
		control.style.height = "20px";
		
		var wControl = this.createControlButton('Waypoints', this.showWaypoints);
		var tControl = this.createControlButton('Tracks', this.showTracks);
		var iControl = this.createControlButton('Images', this.showImages);
		
		control.appendChild(wControl);
		control.appendChild(tControl);
		control.appendChild(iControl);
		
		this.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(control);
	},
	
	createMarker : function(obj, map) {
		var lat = parseFloat(obj.latitude);
		var lng = parseFloat(obj.longitude);
		var pt = new google.maps.LatLng(lat,lng);
		
		var iconImg = this.createMarkerIcon(obj.icon);

		var iconShadow = this.createMarkerShadow(obj.icon_shadow);
		
		var marker = new google.maps.Marker({
			map: map,
			position: pt,
			title: obj.title,
			icon: iconImg,
			shadow: iconShadow
		});
		
		return marker;
	},
	
	createPointMarker : function(point, map) {
		var lat = parseFloat(point.latitude);
		var lng = parseFloat(point.longitude);
		var pt = new google.maps.LatLng(lat,lng);
		
		var iconImg = new google.maps.MarkerImage(this.iconUrlBase + 'kml/pal4/icon24.png',
			      // This marker is 20 pixels wide by 32 pixels tall.
			      new google.maps.Size(32, 32),
			      // The origin for this image is 0,0.
			      new google.maps.Point(0,0),
			      // The anchor for this image is the base of the flagpole at 16,32.
			      new google.maps.Point(16, 16));;
		
		var marker = new google.maps.Marker({
			map: map,
			position: pt,
			icon: iconImg
		});
		
		return marker;
	},
	
	createMarkerIcon : function(icon) {
		return new google.maps.MarkerImage(this.iconUrlBase + icon,
			      // This marker is 20 pixels wide by 32 pixels tall.
			      new google.maps.Size(32, 32),
			      // The origin for this image is 0,0.
			      new google.maps.Point(0,0),
			      // The anchor for this image is the base of the flagpole at 16,32.
			      new google.maps.Point(16, 32));
	},
	
	createMarkerShadow : function(shadow) {
		return new google.maps.MarkerImage(this.iconUrlBase + shadow,
			      // The shadow image is larger in the horizontal dimension
			      // while the position and offset are the same as for the main image.
			      new google.maps.Size(37, 32),
			      new google.maps.Point(0,0),
			      new google.maps.Point(16, 32));
	},
	
	createPolyline : function(obj, map) {
		var pts = [];
		
		// points
		for(var i = 0; i < obj.points.length; i++) {
			var pt = new google.maps.LatLng(
    				parseFloat(obj.points[i].latitude),
    				parseFloat(obj.points[i].longitude));
			pts[i] = pt;
			this.map_bounds.extend(pt);
		}
		
		var polyline = new google.maps.Polyline({
			map: map,
			path: pts,
			strokeColor: obj.color,
			strokeOpacity: 0.8,
			strokeWeight: 3
		});
		
		return polyline;
	},
	
	createPointPolyline : function(prevPt, curPt, map) {
		var pts = [];
		
		// points
		var pt1 = new google.maps.LatLng(
				parseFloat(prevPt.latitude),
    			parseFloat(prevPt.longitude));
		pts.push(pt1);
		
		var pt2 = new google.maps.LatLng(
				parseFloat(curPt.latitude),
    			parseFloat(curPt.longitude));
		pts.push(pt2);
			
		
		var polyline = new google.maps.Polyline({
			map: map,
			path: pts,
			strokeColor: '#ffff00',
			strokeOpacity: 0.5,
			strokeWeight: 10
		});
		
		return polyline;
	},
	
	createImage : function(img) {
		this.t.images.push(img);
		
		if (this.showImages)
			setMap = this.map;
		else
			setMap = null;
		
		img.marker = this.createMarker(img, setMap);
		this.makeMarkerClick(img);
		
		this.map_bounds.extend(img.marker.getPosition());
		hasData = true;
		
		return img;
	},
	
	createWaypoint : function(wpt) {
		this.t.waypoints.push(wpt);
		
		if (this.showWaypoints)
			setMap = this.map;
		else
			setMap = null;
		
		wpt.marker = this.createMarker(wpt, setMap);
		this.makeMarkerClick(wpt);
		
		this.map_bounds.extend(wpt.marker.getPosition());
		hasData = true;
		
		return wpt;
	},
	
	deleteImage : function(img) {
		for (var i = 0; i < this.t.images.length; i++) {
			if (this.t.images[i].ID == img.ID) {
				// Close infowindow
				this.infoWindow.close();
				// Remove marker from map
				img.marker.setMap(null);
				// Remove from array
				this.t.images.splice(i, 1);
				// Destroy
				img = null;
				break;
			}
		}
	},
	
	deletePoint : function(obj, id) {
		var pt;
		var index = -1;
		for (var i = 0; i < obj.points.length; i++) {
			if (obj.points[i].ID == id) {
				pt = obj.points[i];
				index = i;
				break;
			}
		}
		
		if (index >= 0) {
			// Remove from array
			obj.points.splice(index, 1);
			
			// Destroy
			pt = null;
			
			if (obj.polyline != null) {
				// Get map reference
				var m = obj.polyline.getMap();
				
				// Remove current polyline from map
				obj.polyline.setMap(null);
				
				// Recreate polyline
				obj.polyline = this.createPolyline(obj, m);
			}
		}
	},
	
	deleteTrack : function(trk) {
		for (var i = 0; i < this.t.tracks.length; i++) {
			if (this.t.tracks[i].ID == trk.ID) {
				// Close infowindow
				this.infoWindow.close();
				// Remove polyline from map
				trk.polyline.setMap(null);
				// Remove from array
				this.t.tracks.splice(i, 1);
				// Destroy
				trk = null;
				break;
			}
		}
	},
	
	deleteWaypoint : function(wpt) {
		for (var i = 0; i < this.t.waypoints.length; i++) {
			if (this.t.waypoints[i].ID == wpt.ID) {
				// Close infowindow
				this.infoWindow.close();
				// Remove marker from map
				wpt.marker.setMap(null);
				// Remove from array
				this.t.waypoints.splice(i, 1);
				// Destroy
				wpt = null;
				break;
			}
		}
	},
	
	getImageById : function(id) {
		for (var i = 0; i < this.t.images.length; i++) {
			if (this.t.images[i].ID == id) {
				return this.t.images[i];
			}
		}
		return null;
	},
	
	getPointById : function(obj, id) {
		for (var i = 0; i < obj.points.length; i++) {
			if (obj.points[i].ID == id) {
				return obj.points[i];
			}
		}
		return null;
	},
	
	getTrackById : function(id) {
		for (var i = 0; i < this.t.tracks.length; i++) {
			if (this.t.tracks[i].ID == id) {
				return this.t.tracks[i];
			}
		}
		return null;
	},
	
	getWaypointById : function(id) {
		for (var i = 0; i < this.t.waypoints.length; i++) {
			if (this.t.waypoints[i].ID == id) {
				return this.t.waypoints[i];
			}
		}
		return null;
	},
	
	initializeMap : function() {
		var latlng = new google.maps.LatLng(46.871926,-113.990339);
	    var myOptions = {
	      zoom: 13,
	      center: latlng,
	      navigationControl: true,
	      mapTypeControl: true,
	      mapTypeControlOptions: {
	    		style: google.maps.MapTypeControlStyle.DROPDOWN_MENU },
	      scaleControl: true,
	      mapTypeId: google.maps.MapTypeId.TERRAIN
	    };
	    
	    var canvas = document.getElementById("map-canvas");
	    if (canvas != null) {
		    this.map = new google.maps.Map(canvas, myOptions);
		    this.map_bounds = new google.maps.LatLngBounds();
		    this.infoWindow = new google.maps.InfoWindow({maxWidth: 250});
		    this.createOverlayVisibilityControl();
		    
	    	this.loadTrek();
	    	
	    	google.maps.event.addListener(this.map, 'click', function() {
	    		trekpress.infoWindow.close();
	    	});
	    	
	    }
	},
	
	loadChart: function() {
				
		if (this.t) {
			
			var options = {
				chart: {
					renderTo: 'chart-canvas',
					defaultSeriesType: 'line',
					margin: [50,30,80,80]
	         	},
	         	credits: {
	         		enabled: false
	         	},
	         	legend: {
	         		borderWidth: 0
	         	},
	         	plotOptions: {
	         		line: {
		         		marker: {
		         			enabled: false
		         		}
	         		}
	         	},
		        title: {
	         		text: 'Elevation Profile',
	         		style: {
	         			display: 'block'
	         		}
		        },
		        xAxis: {
		               		title: {
		                	 	text: 'Distance (mi)',
		                	 	enabled: !this.showMetric
		                 	}
		                 },
		        yAxis: {
		                	title: {
		                	 	text: 'Elevation (ft)',
		                	 	enabled: !this.showMetric
		                 	}
		                },
		        series: []
			};
			
			for(var i = 0; i < this.t.tracks.length; i++) {
	    		
				var trk = this.t.tracks[i];
	    		var seriesUS = {
	    			name: trk.title,
	    			data: []
	    		};
	    		
	    		var seriesMetric = {
		    			name: trk.title,
		    			data: []
		    		};
	    		
	    		// Trackpoints
	    		var lastLat = null;
	    		var lastLng = null;
	    		var distance = 0;
	    		for(var j = 0; j < trk.points.length; j++) {
	    			var pt = trk.points[j];
	    			var lat = parseFloat(pt.latitude);
	    			var lng = parseFloat(pt.longitude);
	    			var ele = parseInt(pt.elevation);
	    			
	    			if (j != 0) {
	    				distance += this.getDistance(lastLat, lastLng, lat, lng, 1);
	    			}
	    			lastLat = lat;
	    			lastLng = lng;
	    			seriesUS.data.push([this.roundTo(this.getMilesFromKilometers(distance), 2), this.roundTo(this.getFeetFromMeters(ele), 0)]);
	    			seriesMetric.data.push([this.roundTo(distance, 2), this.roundTo(ele, 0)]);
	    		}
	    		trk.seriesUS = seriesUS;
	    		trk.seriesMetric = seriesMetric;
	    		
	    		if (this.showMetric)
	    			options.series.push(seriesMetric);
	    		else
	    			options.series.push(seriesUS);
	    	}
			
			this.chart = new Highcharts.Chart(options);
		}
	},
	
	loadTrek : function() {
		if (this.t) {
			var hasData = false;
			var setMap;
			
			// Waypoints
	    	for(var i = 0; i < this.t.waypoints.length; i++) {
	    		var wpt = this.t.waypoints[i];
	    			    			    		
	    		if (this.showWaypoints)
	    			setMap = this.map;
	    		else
	    			setMap = null;
	    		
	    		wpt.marker = this.createMarker(wpt, setMap);
	    		this.makeMarkerClick(wpt);
	    		
	    		this.map_bounds.extend(wpt.marker.getPosition());
	    		hasData = true;
	    	}
	    	
	    	// Tracks
	    	for(var j = 0; j < this.t.tracks.length; j++) {
	    		var trk = this.t.tracks[j];
	    			    		
	    		if (this.showTracks)
	    			setMap = this.map;
	    		else
	    			setMap = null;	    		
	    		
	    		trk.polyline = this.createPolyline(trk, setMap);
	    		this.makePolylineClick(trk);
	    		hasData = true;
	    	}
	    	
	    	// Images
	    	for(var k = 0; k < this.t.images.length; k++) {
	    		var img = this.t.images[k];
	    			    		
	    		if (this.showImages)
	    			setMap = this.map;
	    		else
	    			setMap = null;

	    		img.marker = this.createMarker(img, setMap);
	    		this.makeImageClick(img);
	    		
	    		this.map_bounds.extend(img.marker.getPosition());
	    		hasData = true;
	    	}
	    	
	    	if (hasData) {
	    		this.map.fitBounds(this.map_bounds);
	    	}
	    }
	},
	
	makeImageClick : function(img) {
		google.maps.event.addListener(img.marker, 'click', function(event) {
			trekpress.openImageInfoWindow(img);
		});
	},
	
	makeMarkerClick : function(wpt) {
		google.maps.event.addListener(wpt.marker, 'click', function(event) {
			trekpress.openMarkerInfoWindow(wpt);
		});
	},
	
	makePolylineClick : function(trk) {
		google.maps.event.addListener(trk.polyline, 'click', function(event) {
			trekpress.openPolylineInfoWindow(trk, event.latLng);
		});
	},
	
	newImage : function() {
		var center = this.map.getCenter();
		var img = {
			title: "Image",
			latitude: center.lat(),
			longitude: center.lng(),
			elevation: 0,
			description: '',
			thumbnail_url: '',
			image_url: '',
			icon: 'kml/pal4/icon46.png',
			icon_shadow: 'kml/pal4/icon46s.png'
		};
		
		return img;
	},
	
	newWaypoint : function() {
		var center = this.map.getCenter();
		var wpt = {
			title: "Waypoint",
			latitude: center.lat(),
			longitude: center.lng(),
			elevation: 0,
			description: '',
			comment: '',
			icon: 'kml/paddle/red-blank_maps.png',
			icon_shadow: 'ms/icons/msmarker.shadow.png'
		};
		
		return wpt;
	},
	
	openImageInfoWindow : function(img) {
		var contentString = 
			'<div class="info-window-content">' +
			'<p>' + img.description + '</p>' +
			'<p style="text-align:center; margin: 0 auto;">' +
			'<a href="' + img.image_url + '">' +
			'<img src="' + img.thumbnail_url + '" width="150" height="150" /></a></p>' + 
			'</div>';
		
		this.infoWindow.setContent(contentString);
		this.infoWindow.open(this.map, img.marker);
	},
	
	openMarkerInfoWindow : function(wpt) {
		var contentString = 
			'<div class="info-window-content">' +
			'<h2>' + wpt.title + '</h1>' +
			'<p><strong>Description: </strong>' + wpt.description + '</p>' +
			'<p><strong>Comment: </strong>' + wpt.comment + '</p>' +
			'</div>';
		
		this.infoWindow.setContent(contentString);
		this.infoWindow.open(this.map, wpt.marker);
	},
	
	openPolylineInfoWindow : function(trk, pt) {
		var contentString = 
			'<div class="info-window-content">' +
			'<h2>' + trk.title + '</h1>' +
			'</div>';
		
		this.infoWindow.setContent(contentString);
		
		if (pt == null) {
			this.infoWindow.setPosition(trk.polyline.getPath().getAt(0));
		} else {
			this.infoWindow.setPosition(pt);
		}
		this.infoWindow.open(this.map);
	},
	
	reducePoints : function(trk, interval, unit) {
		var ids = [];
		var pts = [];
		
		var u;
		if (unit == 'ft')
			u = this.units.feet;
		else
			u = this.units.meters;
				
		pts.push(trk.points[0]);
		for(var i = 1; i < trk.points.length; i++) {
			// compare by distance
			var dist = this.getDistance(parseFloat(pts[pts.length-1].latitude), parseFloat(pts[pts.length-1].longitude), 
					parseFloat(trk.points[i].latitude), parseFloat(trk.points[i].longitude), u);
			if (dist >= interval) { 
				// save it because distance is greater than interval
				pts.push(trk.points[i]);
			} else { 
				// eliminate it because distance is less than interval
				ids.push(trk.points[i].ID);
			} 
		}
		
		trk.points = pts;
		
		return ids;
	},
	
	toggleChart: function() {
		this.showMetric = !this.showMetric;
		
		if (this.showMetric) {
			
			for(var i = 0; i < this.t.tracks.length; i++) {
				this.chart.series[i].remove(false);
				this.chart.addSeries(this.t.tracks[i].seriesMetric, false);
			}
			
		} else {
			
			for(var i = 0; i < this.t.tracks.length; i++) {
				this.chart.series[i].remove(false);
				this.chart.addSeries(this.t.tracks[i].seriesUS, false);
			}
						
		}
		
		if (this.showMetric) {
			this.chart.xAxis[0].options.title.text = "Distance (km)";
			this.chart.yAxis[0].options.title.text = "Elevaton (m)";
		} else {
			this.chart.xAxis[0].options.title.text = "Distance (mi)";
			this.chart.yAxis[0].options.title.text = "Elevation (ft)";
		}
		this.chart.redraw();
	},
	
	toggleOverlayVisibility: function(type) {
		if (type == 'Waypoints') {
			this.showWaypoints = !this.showWaypoints;
			for(var i = 0; i < this.t.waypoints.length; i++) {
				if (this.showWaypoints) {
					if (this.t.waypoints[i].marker != this.realMarker) {
						this.t.waypoints[i].marker.setMap(this.map);
					}
				} else {
					this.t.waypoints[i].marker.setMap(null);
				}
			}
		} else if (type == 'Tracks') {
			this.showTracks = !this.showTracks;
			for(var j = 0; j < this.t.tracks.length; j++) {
				if (this.showTracks) {
					if (this.t.tracks[j].polyline != this.realTrack) {
						this.t.tracks[j].polyline.setMap(this.map);
					}
				} else {
					this.t.tracks[j].polyline.setMap(null);
				}
			}
		} else if (type == 'Images') {
			this.showImages = !this.showImages
			for(var l = 0; l < this.t.images.length; l++) {
				if (this.showImages) {
					if (this.t.images[l].marker != this.realImgMarker) {
						this.t.images[l].marker.setMap(this.map);
					}
				} else {
					this.t.images[l].marker.setMap(null);
				}
			}
		}
	},
	
	updateImage : function(img) {
		var i = this.getImageById(img.ID);
		i.title = img.title;
		i.description = img.description;
		i.latitude = img.latitude;
		i.longitude = img.longitude;
		i.thumbnail_url = img.thumbnail_url;
		i.image_url = img.image_url;
		i.icon = img.icon;
		i.icon_shadow = img.icon_shadow;
		
		i.marker.setIcon(this.createMarkerIcon(i.icon));
		i.marker.setShadow(this.createMarkerShadow(i.icon_shadow));		
		i.marker.setPosition(new google.maps.LatLng(i.latitude, i.longitude));
		
		this.infoWindow.close();
		this.openImageInfoWindow(i);
		
		return i;
	},
	
	updateTrack : function(trk) {
		var t = this.getTrackById(trk.ID);
		t.title = trk.title;
		t.color = trk.color;
		t.points = trk.points;
		
		t.polyline.setMap(null);
		t.polyline = this.createPolyline(t, null);
		
		this.infoWindow.close();
		
		return t;
	},
	
	updateWaypoint : function(wpt) {
		var w = this.getWaypointById(wpt.ID);
		w.title = wpt.title;
		w.latitude = wpt.latitude;
		w.longitude = wpt.longitude;
		w.description = wpt.description;
		w.comment = wpt.comment;
		w.icon = wpt.icon;
		w.icon_shadow = wpt.icon_shadow;
		
		w.marker.setIcon(this.createMarkerIcon(w.icon));
		w.marker.setShadow(this.createMarkerShadow(w.icon_shadow));		
		w.marker.setPosition(new google.maps.LatLng(w.latitude, w.longitude));
				
		this.infoWindow.close();
		this.openMarkerInfoWindow(w);
		
		return w;
	},
	
	//
	// Distance Formula - From: http://www.faqs.org/faqs/geography/infosystems-faq/
	// -----------------------------
	// dlon = lon2 - lon1
    // dlat = lat2 - lat1
	// R (in mi) = 3963 - 13 * sin(lat) where lat is a latitude near which the bulk of your calculations occur. (so I avg it)
    // a = sin^2(dlat/2) + cos(lat1) * cos(lat2) * sin^2(dlon/2)
    // c = 2 * arcsin(min(1,sqrt(a)))
    // d = R * c (in mi)
	//
	getDistance: function (lat1, lng1, lat2, lng2, unit) {
		
		if (unit == null)
			unit = 0;
		
		var avgLat = (lat1 + lat2) / 2;
		var R = 3963 - (13 * Math.sin(this.deg2rad(avgLat)));
		var dlat = lat2 - lat1;
		var dlng = lng2 - lng1;
		var a = Math.pow(Math.sin(this.deg2rad(dlat/2)), 2) + Math.cos(this.deg2rad(lat1)) * Math.cos(this.deg2rad(lat2)) * Math.pow(Math.sin(this.deg2rad(dlng/2)), 2);
		var c = 2 * Math.asin(Math.min(1, Math.sqrt(a)));
		var dist = R * c; // in miles
		
		if(unit == this.units.kilometers) {
			dist = dist * 1.609344;
		} else if (unit == this.units.feet) {
			dist = dist * 5280;
		} else if (unit == this.units.meters) {
			dist = dist * 1609.344;
		}
		
		return dist;
	},
	
	deg2rad: function(deg) {
		return (deg * Math.PI / 180.0);
	},
	
	rad2deg: function(rad) {
		return (rad / Math.PI * 180.0);
	},
	
	getMilesFromKilometers: function(kilometers) {
		return kilometers * 0.621371192;
	},
	
	getFeetFromMeters: function(meters) {
		return meters * 3.2808399;
	},
	
	roundTo: function(num, digits) {
		return Math.round(num * Math.pow(10, digits))/Math.pow(10, digits);
	},
		
	/**
	*  Adapted from:
	*  UTF-8 data encode / decode
	*  http://www.webtoolkit.info/
	*
	**/
	// public method for url encoding
	encodeUtf8 : function (string) {
		string = string.replace(/\r\n/g,"\n");
		var utftext = "";
 
		for (var n = 0; n < string.length; n++) {
 
			var c = string.charCodeAt(n);
 
			if (c < 128) {
				utftext += String.fromCharCode(c);
			}
			else if((c > 127) && (c < 2048)) {
				utftext += String.fromCharCode((c >> 6) | 192);
				utftext += String.fromCharCode((c & 63) | 128);
			}
			else {
				utftext += String.fromCharCode((c >> 12) | 224);
				utftext += String.fromCharCode(((c >> 6) & 63) | 128);
				utftext += String.fromCharCode((c & 63) | 128);
			}
 
		}
 
		return utftext;
	},
 
	// public method for url decoding
	decodeUtf8 : function (utftext) {
		var string = "";
		var i = 0;
		var c = c1 = c2 = 0;
 
		while ( i < utftext.length ) {
 
			c = utftext.charCodeAt(i);
 
			if (c < 128) {
				string += String.fromCharCode(c);
				i++;
			}
			else if((c > 191) && (c < 224)) {
				c2 = utftext.charCodeAt(i+1);
				string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
				i += 2;
			}
			else {
				c2 = utftext.charCodeAt(i+1);
				c3 = utftext.charCodeAt(i+2);
				string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
				i += 3;
			}
 
		}
 
		return string;
	}
	
}