//<![CDATA[

	var reasons = new Array();
	reasons[G_GEO_SUCCESS]            = "Success";
	reasons[G_GEO_MISSING_ADDRESS]    = "Missing Address: The address was either missing or had no value.";
	reasons[G_GEO_UNKNOWN_ADDRESS]    = "Unknown Address:  No corresponding geographic location could be found for the specified address.";
	reasons[G_GEO_UNAVAILABLE_ADDRESS]= "Unavailable Address:  The geocode for the given address cannot be returned due to legal or contractual reasons.";
	reasons[G_GEO_BAD_KEY]            = "Bad Key: The API key is either invalid or does not match the domain for which it was given";
	reasons[G_GEO_TOO_MANY_QUERIES]   = "Too Many Queries: The daily geocoding quota for this site has been exceeded.";
	reasons[G_GEO_SERVER_ERROR]       = "Server error: The geocoding request could not be successfully processed.";

	var empPri = new Array();
	var empLoc = new Array();
	var officeLoc = new Array();
	var officeScore = new Array();
	var officeCommute = new Array();
	var officeSorted = new Array();
	var geocoder = new GClientGeocoder();
	var trafficOverlay; 
	var geocodeLock = false;
	var maxChunkSize = 24;
	var timeout = 2000;
	var currOffice = 0;
	var currChunkOffset;
	var wayStr = new Array();
	var gebDirections;
	var map;
	
	function initMap() {
		if (GBrowserIsCompatible()) {
    		map = new GMap2(document.getElementById("map"));
    		map.addControl(new GLargeMapControl());
    		map.addControl(new GMapTypeControl());
    		map.addControl(new GScaleControl());
    		map.addControl(new google.maps.LocalSearch(), new GControlPosition(G_ANCHOR_BOTTOM_RIGHT, new GSize(10,20)));
			if (mode == 1) map.setCenter(empMapLatLng, empMapZoom);
    		else if (mode == 2 || mode == 3) map.setCenter(offMapLatLng, offMapZoom);
    		trafficOverlay = new GTrafficOverlay();
    		if (showTraffic) {
      			map.addOverlay(trafficOverlay);
      			trafficOverlay.show();
    		}
    		GEvent.addListener(map, "click", function(marker, latLng) {
				if (marker == null) {
					if (mode == 1) {
	  					var freeInd = empAddrArr.length;
	  					for (var i = 0; i < empAddrArr.length; i++) {
	    					if (isEmpty(mode, i)) {
	      						freeInd = i;
	      						break;
	    					} 
	  					}
						
	  					empAddrArr[freeInd] = latLng.toString();
	  					empGeoStatus[freeInd] = 1;
	  					empLatLngArr[freeInd] = latLng;
	  					setDefaultIfMissing(mode, freeInd);
						document.getElementById('empAddr'+freeInd).value = latLng.toString();
	  					if (freeInd >= empNumRows - 1) 
	    					addRow(mode);
					} else if (mode == 2) {
	  					var freeInd = offAddrArr.length;
	  					for (var i = 0; i < offAddrArr.length; i++) {
	    					if (isEmpty(mode, i)) {
	      						freeInd = i;
	      						break;
	    					}
	  					}
	  					offAddrArr[freeInd] = latLng.toString();
	  					offGeoStatus[freeInd] = 1;
	  					offLatLngArr[freeInd] = latLng;
	  					setDefaultIfMissing(mode, freeInd);
	  					document.getElementById('offAddr'+freeInd).value = latLng.toString();
						if (freeInd >= offNumRows - 1)
	    					addRow(mode);
					}
					updateFieldStatus(mode,freeInd);
      			}
				
    		});
    		GEvent.addListener(map, "moveend", function() {
				var center = map.getCenter();
				if (mode == 1) empMapLatLng = center;
				else if (mode == 2 || mode == 3) offMapLatLng = center;
      		});
    
    		GEvent.addListener(map, "zoomend", function(oldLevel, newLevel) {
				if (mode == 1) empMapZoom = newLevel;
				else if (mode == 2 || mode == 3) offMapZoom = newLevel;
      		});
  		}
	}

	function updateMap() {
  		map.clearOverlays();
  		if (mode == 1) {
    		for (var i = 0; i < empLatLngArr.length; i++) {
      			if (empLatLngArr[i] != null) {
					map.addOverlay(new GMarker(empLatLngArr[i]), G_DEFAULT_ICON);
      			}
    		}
  		} else if (mode == 2) {
    		for (var i = 0; i < offLatLngArr.length; i++) {
      			if (offLatLngArr[i] != null) {
					map.addOverlay(new GMarker(offLatLngArr[i]), G_DEFAULT_ICON);
      			}
    		}
  		} else if (mode == 3) {
    		var oneThird = Math.ceil(resRankArr.length/3);
    		var twoThird = Math.ceil(2*resRankArr.length/3);
    		var iconStr;
    		for (var i = 0; i < resRankArr.length; i++) {
      			if (i < oneThird) iconStr = "../map-icons/icong" + (i+1) + ".png";
      			else if (i >= oneThird && i < twoThird) iconStr = "../map-icons/icony" + (i+1) + ".png";
      			else if (i >= twoThird) iconStr = "../map-icons/iconr" + (i+1) + ".png";
      			var icon = new GIcon(G_DEFAULT_ICON, iconStr);
      			map.addOverlay(new GMarker(offLatLngArr[resRankArr[i]], icon));
    		}    
  		}
  		if (mode == 1) map.setCenter(empMapLatLng, empMapZoom);
  		else if (mode == 2 || mode == 3) map.setCenter(offMapLatLng, offMapZoom);
  		if (showTraffic) {
    		trafficOverlay = new GTrafficOverlay();
    		map.addOverlay(trafficOverlay);
    		trafficOverlay.show();
  		}
	}

	function checkEntry(num) {
	  	var currLength = (mode == 1) ? empAddrArr.length : offAddrArr.length;
	  	if (num >= 0 && num < currLength) {
	    	var address = (mode == 1) ? empAddrArr[num] : offAddrArr[num];
	    	if (!isLatLngStr(address)) {
	      		getLatLng(mode, num, address);
	    	} else {
	      		if (mode == 1) {
					empLatLngArr[num] = str2latLng(address);
					empGeoStatus[num] = 1;
	      		} else if (mode == 2) {
					offLatLngArr[num] = str2latLng(address);
					offGeoStatus[num] = 1;
	      		}
				updateFieldStatus(mode,num);
	    	}
	  	}
	}

	function formatTime(seconds) {
		seconds = Math.ceil(seconds);
	  	var days;
	  	var hours;
	  	var minutes;
	  	days = parseInt(seconds / (24*3600));
	  	seconds -= days * 24 * 3600;
	  	hours = parseInt(seconds / 3600);
	  	seconds -= hours * 3600;
	  	minutes = parseInt(seconds / 60);
	  	seconds -= minutes * 60;
	  	var ret = "";
	  	if (days > 0) 
	    	ret += days + " days ";
	  	if (days > 0 || hours > 0) 
	    	ret += hours + " hrs ";
	  	if (days > 0 || hours > 0 || minutes > 0) 
	    	ret += minutes + " min ";
	  	if (days == 0 && hours == 0)
	    	ret += seconds + " sec";
	  	return(ret);
	}
	
	function formatLength(meters) {
		var km = parseInt(meters / 1000);
		meters -= km * 1000;
		var ret = "";
		if (km > 0) 
	    	ret += km + " km ";
		if (km < 10)
	    	ret += meters + " m";
	  return(ret);
	}
	
	function isLatLngStr(str) {
		return(str.match(/\(\s*\-?([0-9]+|[0-9]*\.[0-9]+),\s*\-?([0-9]+|[0-9]*\.[0-9]+)\)/));
	}

	function str2latLng(str) {
	  	var arr = str.split(/[\s\(\),]+/);
	  	return(new GLatLng(arr[1],arr[2]));
	}
	
	function getLatLng(modeLocal, num, address) {
  		if (geocodeLock) {
    		setTimeout("getLatLng(" + modeLocal + ", " + num + ", '" + address + "')",500);
  		} else {
    		geocodeLock = true;
    		geocoder.getLatLng(address, function(latLng) {
				geocodeLock = false;
				if (!latLng) {
	  				if (modeLocal == 1) {
	    				empLatLngArr[num] = null;
	    				empGeoStatus[num] = 2;
	  				} else if (modeLocal == 2) {
	    				offLatLngArr[num] = null;
	    				offGeoStatus[num] = 2;
	  				}
				} else {
	  				if (modeLocal == 1) {
	    				empLatLngArr[num] = latLng;
	    				empGeoStatus[num] = 1;
	    				var bounds = getBounds(empLatLngArr);
	    				empMapLatLng = bounds.getCenter();
	    				empMapZoom = map.getBoundsZoomLevel(bounds);
	  				} else if (modeLocal == 2) {
	    				offLatLngArr[num] = latLng;
	    				offGeoStatus[num] = 1;
	    				var bounds = getBounds(offLatLngArr);
	    				offMapLatLng = bounds.getCenter();
	    				offMapZoom = map.getBoundsZoomLevel(bounds);
	  				}
				}
				updateFieldStatus(modeLocal,num);
      		});
  		}
	}
	
	function getBounds(locations) {
  		var bounds = new GLatLngBounds();
  		for (var i = 0; i < locations.length; i++) {
    		if (locations[i] != null) {
      			bounds.extend(locations[i]);
    		}
  		}
  		return(bounds);
	}
	
	function getAddressStr(street, city, state, zip, country) {
	  	var address = "";
	  	var part = new Array();
	  	part[0] = street;
	  	part[1] = city;
	  	part[2] = state;
	  	part[3] = zip;
	  	part[4] = country;
	  	for (var i = 0; i < part.length; i++) {
	    	if (part[i] != "") {
	      		if (address != "") address += ", ";
	      		address += part[i];
	    	}
	  	}
	  	return(address);
	}
	
	function getWayStr(officeNum) {
		var nextAbove = -1;
		for(var i=0;i<empLoc.length;++i) {
			wayStr.push(makeLatLng(officeLoc[officeNum]));
			wayStr.push(makeLatLng(empLoc[i]));
		}
		wayStr.push(makeLatLng(officeLoc[officeNum]));
	}
	
	function makeLatLng(latLng) {
		return(latLng.toString().substr(1,latLng.toString().length-2));
	}
	
	function isEmpty(mode, num) {
	  	if (mode == 1) {
	    	return(empNameArr[num] == "" && empAddrArr[num] == "");
	  	} else if (mode == 2) {
	    	return(offNameArr[num] == "" && offAddrArr[num] == "");
	  	}
	}
	
	function setDefaultIfMissing(mode, num) {
	  	if (mode == 1) {
	    	if (empNameArr[num] == null) {
	      		empNameArr[num] = "";
	    	}
	    	if (empAddrArr[num] == null) {
	      		empAddrArr[num] = "";
	    	}
	    	if (empPriArr[num] == null) {
	      		empPriArr[num] = 1;
	    	}
	    	if (empGeoStatus[num] == null) {
	      		empGeoStatus[num] = 0;
	    	}
	  	} else if (mode == 2) {
	    	if (offNameArr[num] == null) {
	      		offNameArr[num] = "";
	    	}
	    	if (offAddrArr[num] == null) {
	      		offAddrArr[num] = "";
	    	}
	    	if (offGeoStatus[num] == null) {
	      		offGeoStatus[num] = 0;
	    	}
	  	}
	  	if (isEmpty(mode, num)) {
	    	if (mode == 1) {
	      		empGeoStatus[num] = 0;
	      		empLatLngArr[num] = null;
	    	} else if (mode == 2) {
	      		offGeoStatus[num] = 0;
	      		offLatLngArr[num] = null;
	    	}
	    	updateMap();
	  	}
	}
	
	function toggleTrafficOverlay() {
	  	if (showTraffic == 1) {
	    	map.removeOverlay(trafficOverlay);
	    	showTraffic = 0;
	  	} else {
	    	map.addOverlay(trafficOverlay);
	    	trafficOverlay.show();
	    	showTraffic = 1;
	  	}
	}
	
	function updateFieldStatus(mode,num) {
		var style = new Array;
		style[0] = '2px solid #ccc';
		style[1] = '2px solid #0f0';
		style[2] = '2px solid #f00';
		style[3] = '2px solid #ccc';
		if(mode == 1) {
			var elem = document.getElementById('empAddr'+num);
			elem.style.border = style[empGeoStatus[num]];
		} else if(mode == 2) {
			var elem = document.getElementById('offAddr'+num);
			elem.style.border = style[offGeoStatus[num]];
		}
		updateMap();
	}
	
	function showBestOffice() {
		var offAct = 0;
		var empAct = 0;
		var offToActive = new Array();
		var empToActive = new Array();
		for(var i=0;i<offLatLngArr.length;i++) {
			if(offLatLngArr[i] != null) {
				offToActive[offAct] = i;
				resRouteArr[i] = new Array();
				empAct = 0;
				for(var j=0;j<empLatLngArr.length;j++) {
					if(empLatLngArr[j] != null) {
						empToActive[empAct] = j;
						resRouteArr[i][j] = new Array();
						resRouteArr[i][j][0] = officeCommute[offAct][empAct][0];
						resRouteArr[i][j][1] = officeCommute[offAct][empAct][1];
						empAct++;
					}
				}
				offAct++;
			}
		}		
		//sort by distance (asc)
		var officeRank = new Array(officeScore.length);
		for(var i=0;i<officeRank.length;i++) {
			officeRank[i] = -1;
		}
		for(var i=0;i<officeRank.length;i++) {
			var bestj = -1;
			var bestScore = 1e9;
			for(var j=0;j<officeRank.length;j++) {
				if(officeRank[j] == -1 && officeScore[j] < bestScore) {
					bestScore = officeScore[j];
					bestj = j;
				}
			}
			officeRank[bestj] = i;
		}
		//get order of offices
		var officeSorted = new Array(officeRank.length);
		for(var i=0;i<officeRank.length;i++) {
			officeSorted[officeRank[i]] = i;
		}
		//store "real" office indices
		resRankArr = new Array();
		for(var i=0;i<officeSorted.length;i++) {
			resRankArr[i] = offToActive[officeSorted[i]];
		}
		map.clearOverlays();
		var sumEmpPri = 0.0;
		for(var i=0;i<empPri.length;i++) {
			sumEmpPri += empPri[i] * 1.0;
		}
		//mark rank[0,num/3] as green
		var oneThird = Math.ceil(officeScore.length/3);
		var twoThird = Math.ceil(2*officeScore.length/3);
		var iconStr;
		var summaryStr = "";
		for (var i = 0; i < officeScore.length; i++) {
			if (i < oneThird) 
		      	iconStr = "map-icons/icong" + (i+1) + ".png";
		    else if (i >= oneThird && i < twoThird) 
		      	iconStr = "map-icons/icony" + (i+1) + ".png";
		    else if (i >= twoThird) 
		      	iconStr = "map-icons/iconr" + (i+1) + ".png";
			var off = offToActive[officeSorted[i]];
			summaryStr += '<tr onmouseover="rowHover(this)" onmouseout="rowUnHover(this)">';
			summaryStr += '<td><img src="'+ iconStr +'" alt="" /></td>';
			summaryStr += '<td><a href="#" onclick="toggleOfficeDetails(\'' + off + '\'); return false;">'+ ((offNameArr[off] == "") ? "Office #" + (off+1) : offNameArr[off]) +'</a></td>';
			summaryStr += '<td>'+ formatTime(officeScore[officeSorted[i]] / sumEmpPri) +'</td>';
			summaryStr += '</tr>';
		}
		
		resDivStr = '<table id="result-table" class="data"><thead><tr><th width="50">Score</th><th>Office Name/ID:</th><th width="30%">Avg. Weighted Commute</th></tr></thead><tbody id="result-data">';
		resDivStr += summaryStr;
		resDivStr += '</tbody></table>';
		document.getElementById("status-indicator").style.display = "none";
		document.getElementById('results').innerHTML = resDivStr;
		new Effect.SlideDown('step3');
		updateMap();
	}
	
	function getBestOffice() {
		officeLoc = new Array();
		empLoc = new Array();
		empPri = new Array();
		for(var i=0;i<offLatLngArr.length;i++) {
			if(offLatLngArr[i] != null) {
				officeLoc.push(offLatLngArr[i]);
			}
		}
		for(var i=0;i<empLatLngArr.length;i++) {
			if(empLatLngArr[i] != null) {
				empLoc.push(empLatLngArr[i]);
				empPri.push(empPriArr[i]);
			}
		}
		officeScore = new Array();
		currOffice = 0;
		document.getElementById('status-indicator').style.display = '';
		evaluateOffice();
	}
	
	function evaluateOffice() {
		if(currOffice == officeLoc.length) {
			showBestOffice();
		} else {
			wayStr = new Array();
			document.getElementById('status-indicator').innerHTML = 'Processing office location ' + (currOffice+1) + ' of ' + officeLoc.length + '...\n';
			getWayStr(currOffice);
			currChunkOffset = 0;
			officeScore[currOffice] = 0.0;
			officeCommute[currOffice] = new Array();
			nextChunk();
		}
	}
	
	function nextChunk() {
		if (currChunkOffset < wayStr.length) {
    		var wayStrChunk = new Array();
    		for (var i = 0; i < maxChunkSize && i + currChunkOffset < wayStr.length; ++i) {
      			wayStrChunk.push(wayStr[currChunkOffset+i]);
    		}
    		gebDirections = new GDirections();
    		GEvent.addListener(gebDirections, "error", function() {
				alert("Request failed: " + reasons[gebDirections.getStatus().code]);
      		});
    		GEvent.addListener(gebDirections, "load", function() {
				for (var i = 0; i < gebDirections.getNumRoutes(); ++i) {
	  				var duration = gebDirections.getRoute(i).getDuration().seconds;
	  				var currEmp = Math.floor((currChunkOffset+i) / 2);
	  				if ((currChunkOffset+i) % 2 == 0) {
	    				officeCommute[currOffice][currEmp] = new Array();
	  				}
	  				officeScore[currOffice] += empPri[currEmp] * duration * 0.5;
	  				officeCommute[currOffice][currEmp][(currChunkOffset+i) % 2] = gebDirections.getRoute(i);
				}
				currChunkOffset += maxChunkSize;
				if (currChunkOffset < wayStr.length-1) {
	  				currChunkOffset--;
				}
				setTimeout('nextChunk()',timeout);
      		});
    		gebDirections.loadFromWaypoints(wayStrChunk, { getSteps:true, preserveViewport:true });
  		} else {
    		currOffice++;
    		setTimeout('evaluateOffice()',timeout);
  		}
	}

//]]>

