/*
 * myLieu.js
 * 
 * Part of the project myLieu from Markus Schepp for c't.
 * 03.04.2009
 * 
 * 
 * This file contains client-side JavaScript-functions and
 *  classes.
 * 
 *  10.12.2009: Adding webclient-access through toggleRadar
 *  and the new JavaScript-file myLieuClient.js
 */

// default geoengine is OSM
var theGeoEngine = "osm";
// default layer is Osmarender
var theGeoLayer = "osmarender";


// only if the google-api is proper called in the header of the html-page, this works!
if (typeof google != 'undefined')
{
	// in chrome google is defined, so we have to check, if the
	// function maps is defined to verify, if google-maps or openStreetMap is used
	if (typeof google.load == 'function')
	{
		google.load("maps", "2.x");
		// change engine to google
		theGeoEngine = "google";
	}
}

// indicator if the automatic updater is running
var bUpdaterRunning = true;


/**
 * This one shows the difference between the last timestamp and now
 * with an indicator.
 */
var myTimeDifference = Class.create();

myTimeDifference.prototype=
{
	initialize:function()
	{
		this.myTimeDiff = 0;
	},
	setTimeStamp:function(aTimeStamp)
	{
		var myLastTimeConverted = new Date();
		myLastTimeConverted.setISO8601(aTimeStamp);
		var myLocalTime = new Date();
		this.myTimeDiff = Math.floor((myLocalTime.getTime() - myLastTimeConverted.getTime())/(1000));
	},
	/**
	 * returns the timedifference as ISO8601
	*/
	getTimeStamp:function()
	{
		return this.myTimeDiff;
	},
	/** 
	 * Returns a string representing the time differnce, like "one hour ago" or "yesterday"
	 */
	getTimeString:function()
	{
		var getTimeString = "";
		var myTimePhrase = "Sekunden";

		// take the seconds as default
		var myTimeDiffCorrected = this.myTimeDiff;
		
		if (myTimeDiffCorrected < 0)
		{
			getTimeString = "kleiner 0!";
		}
		
		if (myTimeDiffCorrected == 1)
		{
			myTimePhrase = "Sekunde";
			
			getTimeString = myTimeDiffCorrected + ' ' + myTimePhrase;
		}

		// now change to "min", "hours" or "days" or "weeks" or "months"
		// correction to minutes
		if ( (this.myTimeDiff/60) >= 1)
		{
			myTimePhrase = "Minuten";
			myTimeDiffCorrected = Math.floor(this.myTimeDiff/60);
			if (myTimeDiffCorrected == 1)
			{
				myTimePhrase = "Minute";
			}
			
			getTimeString = myTimeDiffCorrected + ' ' + myTimePhrase;
		}

		// correction to hours
		if ( (this.myTimeDiff/(60*60)) >= 1)
		{
			myTimePhrase = "Stunden";
			myTimeDiffCorrected = Math.floor(this.myTimeDiff/(60*60));
			if (myTimeDiffCorrected == 1)
			{
				myTimePhrase = "Stunde";
			}
			
			getTimeString = myTimeDiffCorrected + ' ' + myTimePhrase;
		}

		// correction to days
		if ( (this.myTimeDiff/(60*60*24)) >= 1)
		{
			myTimePhrase = "Tage";
			myTimeDiffCorrected = Math.floor(this.myTimeDiff/(60*60*24));
			
			switch (myTimeDiffCorrected)
			{
				case 1:
					myTimePhrase = "Gestern";
					getTimeString = myTimePhrase;
					break;
				case 2:
					myTimePhrase = "Vorgestern";
					getTimeString = myTimePhrase;
					break;
				default:
					getTimeString = myTimeDiffCorrected + ' ' + myTimePhrase;
					break;
			}
		}
		
		
		// correction to weeks
		if ( (this.myTimeDiff/(60*60*24*7)) >= 1)
		{
			myTimePhrase = "Wochen";
			myTimeDiffCorrected = Math.floor(this.myTimeDiff/(60*60*24*7));
			
			if (myTimeDiffCorrected == 1)
			{
				myTimePhrase = "Woche";
			}
			
			getTimeString = myTimeDiffCorrected + ' ' + myTimePhrase;
		}
		
		// correction to years
		if ( (this.myTimeDiff/(60*60*24*365)) >= 1)
		{
			myTimePhrase = "Jahre";
			myTimeDiffCorrected = Math.floor(this.myTimeDiff/(60*60*24*365));
			
			if (myTimeDiffCorrected == 1)
			{
				myTimePhrase = "Jahr";
			}
			
			getTimeString = myTimeDiffCorrected + ' ' + myTimePhrase;
		}
		
		
		
		return getTimeString;
	}
};

/**
 * Extend the class date for a function to handle ISO8601-strings
 */
Date.prototype.setISO8601 = function (string) {
    var regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})" +
        "(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?" +
        "(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?";
    var d = string.match(new RegExp(regexp));

    var offset = 0;
    var date = new Date(d[1], 0, 1);

    if (d[3]) { date.setMonth(d[3] - 1); }
    if (d[5]) { date.setDate(d[5]); }
    if (d[7]) { date.setHours(d[7]); }
    if (d[8]) { date.setMinutes(d[8]); }
    if (d[10]) { date.setSeconds(d[10]); }
    if (d[12]) { date.setMilliseconds(Number("0." + d[12]) * 1000); }
    if (d[14]) {
        offset = (Number(d[16]) * 60) + Number(d[17]);
        offset *= ((d[15] == '-') ? 1 : -1);
    }

    // offset -= date.getTimezoneOffset();
    offset = 0;
    time = (Number(date) + (offset * 60 * 1000));
    this.setTime(Number(time));
}




/**
 * An object describing any important things about a user.
 */
var myLieuUser = Class.create();

myLieuUser.prototype=
{
	initialize:function()
	{
		this.myUserMarker = null;
		this.myPosition = null;
		this.myAltitude = 0;
		this.myLastSpeed = 0;
		this.myNumSatellites = 0;
		this.myDirection = 0.0;
		this.myLastSatelliteInfo = 0;
		this.myLastTime = null;
		this.myTimeStampCounter = 0;
		this.myStatus = false;
	},
	// creates an icon for this user. Depends on the username, where the icon lies
	createUserIcon:function()
	{
		// for working with google you may use a shadow-icon
		return aMapEngine.createIcon("usericon.gif", 40, 44, "shadow.png", 83, 44);
	},
	// Creates a marker on the last known position
	createMarker:function(aPosition) 
	{
		return aMapEngine.createMarker(aPosition, this.createUserIcon());
	},
	changePosition:function(aPosition)
	{
		// check, if we got an icon for this user
		if (!this.myUserMarker)
		{
			this.myUserMarker = this.createMarker(aPosition);
			if (aMapEngine)
			{
				aMapEngine.addMarker(this.myUserMarker);
			}
		}
		else
		{
			if (aMapEngine)
			{
				aMapEngine.changeMarker(this.myUserMarker, aPosition);
			}
		}		
		
		if (aMapEngine)
		{
			aMapEngine.map().panTo(aPosition);
		}
		
		return 
	},
	/* trys to fetch the latest known position of this user
	 */
	getLastPosition:function() 
	{
		var myDate = new Date();
		
		var myXMLFile = "log/geodata.xml?" + myDate.getTime();

		var thisInstance = this;
		
		var myAjax = new Ajax.Request(myXMLFile,
		{
			method: 'get',
			onSuccess: function(request) 
			{
				if (request.readyState == 4) 
				{
					var xmlDoc = request.responseXML;
	
					if (!xmlDoc || !xmlDoc.documentElement)
					{
						// no doc, no elements, no fun!
						return;
					}
	
					// read the elements from the xml-file
					var markers = xmlDoc.documentElement.getElementsByTagName("marker");
	
					if (!markers || (markers.length < 1))
					{
						// no markers, no fun!
						return;
					}
	
					// take only the last one
					var myLastElement = markers.length - 1;

					// get the last position in longitude and latitude
					thisInstance.myPosition = aMapEngine.createLatLong(parseFloat(markers[myLastElement].getAttribute("lat")), parseFloat(markers[myLastElement].getAttribute("lng")));
					
					// you may show that in the message-window
					// addMessage("lat: " + parseFloat(markers[myLastElement].getAttribute("lat")) + " lng: " + parseFloat(markers[myLastElement].getAttribute("lng")) );
	
					// get the speed
					thisInstance.myLastSpeed = parseFloat(markers[myLastElement].getAttribute("speed"));
					
					// get the number of satellites
					thisInstance.myNumSatellites = markers[myLastElement].getAttribute("numsat");
					
					// the altitude
					thisInstance.myAltitude  = parseFloat(markers[myLastElement].getAttribute("alt"));
					
					// the direction
					thisInstance.myDirection = parseFloat(markers[myLastElement].getAttribute("dir"));
					
					// and the time
					thisInstance.myTime = markers[myLastElement].getAttribute("time");
					
					// check 5 times if we got a newer time stamp (another time stamp string)
					if (thisInstance.myLastTime == thisInstance.myTime)
					{
						// no, we got not
						thisInstance.myTimeStampCounter++;
					}
					else
					{
						// reset the timestampcounter
						thisInstance.myLastTime = thisInstance.myTime;
						thisInstance.myTimeStampCounter = 0;
					}
					
					// if we got 5 times the same time in the geodata.xml
					// we must asume, that no new data comes in what means
					// that connection is broken or the driver is dead.
					// Show this by changing the status-symbol
					if (thisInstance.myTimeStampCounter > 4)
					{
						// do not let the timestampcounter run into endless
						thisInstance.myTimeStampCounter = 4;
						thisInstance.setStatus(false);
					}
					else
					{
						thisInstance.setStatus(true);
					}
					
					// now change the marker to the new position
					thisInstance.changePosition(thisInstance.myPosition);
					thisInstance.showSpeed();
				}
			}
		});
	},
	setStatus:function(bStatus)
	{
		// only do something, if status is changing
		if (this.myStatus != bStatus)
		{
			var myStatusArea = $("status_area");
			var myStatusString = "";
			
			var myTimeDateArea = $("timedate_area");
			var myTimeDateAreaString = "";
			
			if (bStatus)
			{
				// condition green
				myStatusString = '<img src="green.gif" width="30" border="0">';
			}
			else
			{
				// condition red
				myStatusString = '<img src="red.gif" width="30" border="0">';
				myTimeDateAreaString = this.getTimeDifference();
			}
			
			if (myStatusArea)
			{
				myStatusArea.update(myStatusString);
			}
			
			if (myTimeDateArea)
			{
				myTimeDateArea.update(myTimeDateAreaString);
			}
			
			// remember the new status
			this.myStatus = bStatus;
		}
	},
	showStatus:function(bShow)
	{
		var myStatusArea = $("status_area");
		var myStatusString = "";
		
		var myTimeDateArea = $("timedate_area");
		var myTimeDateAreaString = "";
		
		// when turning off show nothing, when turning on, start with condition red.
		if (bShow)
		{
			this.setStatus(false);
		}
		else
		{
			// turning off showing status
			if (myStatusArea)
			{
				myStatusArea.update(myStatusString);
			}
			
			if (myTimeDateArea)
			{
				myTimeDateArea.update(myTimeDateAreaString);
			}
		}
	},
	showSpeed:function()
	{
		var mySpeedArea = $("speed_area");
		
		if (mySpeedArea)
		{
			mySpeedArea.update(this.myLastSpeed.toFixed(0));
		}
	},
	/**
	 * This function calculates the difference between the time of the last location-given and NOW+
	 */
	getTimeDifference:function()
	{
		var getTimeDifference = ""; 
		var getTimeDifferenceString = new myTimeDifference();
		
		if (this.myLastTime != null)
		{
			var myLastTime = this.myLastTime;
			
			getTimeDifferenceString.setTimeStamp(myLastTime);
		
			getTimeDifference = getTimeDifferenceString.getTimeString();
		}
		
		return getTimeDifference;
	}
};




// the google-panel
var aMapEngine = null;

// create an object which has an Icon and a position
var aUser = new myLieuUser();

function checkPHPversion()
{
	var myDate = new Date();
	
	var myCheckFile = "checker.php";
	
	var myParameter = "action=phpversion";

	var myAjax = new Ajax.Request(myCheckFile,
	{
		method: 'get',
		parameters: myParameter,
		onSuccess: function(request) 
		{
			if (request.responseText < '5.0.0')
			{
				alert("php version on the server is too low: " + request.responseText);
			}
			else
			{
				addMessage("php-version: " + request.responseText);
				// go on with the next check
				onCheck(2);
			}
		}
	});
}

/**
 * Checks, if the class DomDocument is available on the php-server
 */
function checkDomDocument()
{
	var myDate = new Date();
	
	var myCheckFile = "checker.php";
	
	var myParameter = "action=isDomDocument";

	var myAjax = new Ajax.Request(myCheckFile,
	{
		method: 'get',
		parameters: myParameter,
		onSuccess: function(request) 
		{
			if (request.responseText == '0')
			{
				addMessage("DomDocument is NOT available");
				// go on with the alternate check
				onCheck(3);
			}
			else
			{
				addMessage("DomDocument is available");
				// go on with the next check
				onCheck(4);
			}
		}
	});
}

 /**
  * Checks, if the class DomDocument is available on the php-server
  */
 function checkDomXmlOpenFile()
 {
 	var myDate = new Date();
 	
 	var myCheckFile = "checker.php";
 	
 	var myParameter = "action=domxml_open_file";

 	var myAjax = new Ajax.Request(myCheckFile,
 	{
 		method: 'get',
 		parameters: myParameter,
 		onSuccess: function(request) 
 		{
 			if (request.responseText == '0')
 			{
 				alert("Function domxml_open_file is not available");
 			}
 			else
 			{
 				addMessage("Function domxml_open_file is available");
 				// go on with the next check
 				onCheck(4);
 			}
 		}
 	});
 }


/**
 * Checks, if the class DomDocument is available on the php-server
 */
function checkFileCreatePermission()
{
	var myDate = new Date();
	
	var myCheckFile = "checker.php";
	
	var myParameter = "action=filecreatepermission";

	var myAjax = new Ajax.Request(myCheckFile,
	{
		method: 'get',
		parameters: myParameter,
		onSuccess: function(request) 
		{
			if (request.responseText == '0')
			{
				alert("File creation is not possible");
			}
			else
			{
				addMessage("File creation is possible");
				// go on with the next check
				onCheck(99);
			}
		}
	});
}

/**
 * This one is called instead of onLoad at the beginning
 * of this page.
 * We will check here some preconditions for myLieu:
 * - php-Version
 * - Ajax-support
 * - file-writing on the server
 * 
 */
function onCheck(stage)
{
	switch (stage)
	{
		case 1:
			checkPHPversion();
			break;
			
		case 2:
			checkDomDocument();
			break;
			
		case 3:
			checkDomXmlOpenFile();
			break;
			
		case 4:
			checkFileCreatePermission();
			break;
			
		// last thing we do: load myLieu finally
		case 99:
			onLoad();
			break;
			
		case 0:
		default:
			break;
	}
}

function setGeoLayer(anGeoLayer)
{
	if (anGeoLayer != null)
	{
		theGeoLayer = anGeoLayer;
	}
}

/**
 * This one is called when the html-page is loaded.
 * @param string anUser
 * the name of the user you want to focus after loading
 */
function onLoad(anUser)
{	
	aMapEngine = new myGeoEngine();

	aMapEngine.setEngine(theGeoEngine, theGeoLayer);
	
	// resize the map-div to screen of browser
	onResize();

	// start the periodical updater
	onUpdate(true);
	
	// resize the map-div to screen of browser
	onResize();
}

/**
 * This one is a special loader for the viewing page
 */
function onViewLoad()
{
	aMapEngine = new myGeoEngine();

	aMapEngine.setEngine(theGeoEngine, theGeoLayer);

	// resize the map-div to screen of browser
	onResize();
}

/**
 * resizing some div-element to the size of the browser-client-window
 */
function resizeDiv(myDiv)
{
	var myContentArea = $(myDiv);
	
	if (myContentArea != null)
	{
		var myNewHeight = getWindowHeight() + "px";
		myContentArea.style.height = myNewHeight;
		var myNewWidth = getWindowWidth() + "px";
		myContentArea.style.width = myNewWidth;
	}
}


/**
 * resize the inner map to the browser-windowsize
 */
function onResize()
{
	resizeDiv("map");
	
	aMapEngine.checkResize();
}

/**
 * does a refresh of the screen and start again the timer
 * every 2 seconds or whatever the value ist
 * @parameter bActive
 * true to turn on automatic updater
 * false for turning off
 */
function onUpdate()
{
	if (bUpdaterRunning)
	{
		// ask for the last position of the user
		var aPosition = aUser.getLastPosition();
	
		aUser.showSpeed();
		
		bUpdaterRunning = true;
		
		// repeat this every 2 seconds
		window.setTimeout("onUpdate()", 2000);	
	}
	else
	{
		aUser.showStatus(false);
	}
}


/**
 * This shows an Text in the message-div and deletes
 * the previous
 */
function showMessage(aText)
{
	var myTextArea = $("message_text");
	if (myTextArea)
	{
		myTextArea.value = aText;
	}
}

/**
 * This adds an Text in the message-div 
 */
function addMessage(aText)
{
	var myTextArea = $("message_text");
	if (myTextArea)
	{
		myTextArea.value = aText+"\n"+myTextArea.value;
	}
}


/**
 *
 */
function routeChosing(aRouteFileName)
{
	if (aRouteFileName == "0")
	{
		return;
	}
	
	// the user shurely wants to download the gpx-file, which has
	// the same name except the suffix.
	var aRouteFileNameGPX = aRouteFileName.replace(".xml", ".gpx");
	showDownloadLink(aRouteFileNameGPX);
	
	var myAjax = new Ajax.Request("log/"+aRouteFileName,
	{
		method: 'get',
		onSuccess: function(request) 
		{
			if (request.readyState == 4) 
			{
				var xmlDoc = request.responseXML;

				if (!xmlDoc || !xmlDoc.documentElement)
				{
					// no doc, no elements, no fun!
					return;
				}

				// read the elements from the xml-file
				var markers = xmlDoc.documentElement.getElementsByTagName("marker");

				if (!markers || (markers.length < 1))
				{
					// no markers, no fun!
					alert("no markers");
					return;
				}
				
				var points = [];
				
				for (var i = 0; i < markers.length; i++) 
				{
					// get each marker and store the latitude and longitude as a pointset into an array
					point = aMapEngine.createLatLong(parseFloat(markers[i].getAttribute("lat")), parseFloat(markers[i].getAttribute("lng")));
					points.push(point);
				}
				aMapEngine.addPolyLine(points);				
			}
		}
	});
}

/**
 * This one is responsible for showing a link to download
 * the gpx-file
 * @param aFileName
 */
function showDownloadLink(aFileName)
{
	var myLinkArea = $("downloadLink");
	myLinkArea.update("<a href=\"log/" + aFileName + "\">GPX</a>");
}

/** You may switch on or off the webbased myLieu-client.
 * This client works with the geolocation api provided by google.
 */
function toggleRadar()
{
	if ($("radar_image"))
	{
		if ($("radar_image").src.indexOf("_still") >= 0)
		{
			$("radar_image").src = "radar.gif";
			// myClientTimerImp.start();
			// geoStart();
			initGeoLocation();
			
			aUser.showStatus(false);
			
			// but stop the server-polling!
			bUpdaterRunning = false;
		}
		else
		{
			$("radar_image").src = "radar_still.gif";
			// myClientTimerImp.stop();
			
			aUser.showStatus(true);
			
			// and start again the server-polling
			bUpdaterRunning = true;
			onUpdate();

			geoEnd();
		}
	}
}
