// Copyright (c) 2007, 2008, 2009 IonEarth, LLC  All Rights Reserved
// Author: Russ Ryba

// The Global Map Object
var oMap = null;

// The Global Data Object
var data = {};

// Hook into the events
window.onload=Init;
window.onresize=MapResize;

try {
	console.log( 'started' );
} catch( error )
{
	console = {};
	console.log = function( param ) {  }
}

// Simulated Data Settings
var simData = {};
simData.simStartLat = 43.58;
simData.simStartLon = -79.39; 
simData.simStartZoom = 11;
simData.simRadiusMin = 0.04;
simData.simRadiusMax = 0.10;
simData.simStartTime = new Date().valueOf();
simData.simTimeOffset = new Date().valueOf() -simData.simStartTime;
simData.skipMinutes = -2;
simData.shortMinutes = 0.1;
simData.simMinutes = 6;

// Enum
var Enum = {};
Enum.winnerImages = {
	0:"images/unknown_winner.jpg",
	1:"images/race_in_progress.jpg",
	2:"images/us_wins.jpg",
	3:"images/us_wins_2.jpg",
	4:"images/canada_wins.jpg",
	5:"images/canada_wins_2.jpg"
}
// We must WAIT to ANIMATE!  Need 10 minutes of data for good coverage 
// setTimeout( "oMap.ShowMessage( 'Simulation Complete.  Page will reload in 10 seconds!' );", ( simData.simMinutes * 60 ) * 1000 );
// setTimeout( "window.location.reload()", ( simData.simMinutes * 60 + 10 ) * 1000 );


function CenterOnNode( pNode )
{
	var RacerIndex = FindRacerNode( pNode );
	if ( RacerIndex >= 0 )
	{
		try
		{
			var gps = GetVehiclePositionAtTime( 
				data.racers[RacerIndex], 
				GetAnimationTime() );
			if ( null != gps )
				// oMap.SetCenter( new VELatLong( gps.lat, gps.lon ) );
				oMap.PanToLatLong( new VELatLong( gps.lat, gps.lon ) );
		} catch( error )
		{
		}
	}
}
function getUtcNow()
{
        var tUTC = new Date().valueOf() + new Date().getTimezoneOffset() * 60 * 1000;
        return tUTC;

}

// Trail Lengths
var settings = {};
settings.replayMode = false;
settings.trailSegments = 30; // Split it into 9 segments
settings.trailLengthMs = 1000 * 60 * 15; // Show 2 Minutes of trails
settings.animTimer = 0;
settings.animInterval = 1000 * 4;
settings.animMultiplier = 2;

data.dataLoaded = false;

// Show Clocks
settings.raceOffsetMs = -240 * 60 * 1000;
settings.eventClockTimer = 0;
settings.eventDelayTimeMs = 60 * 1000 * 2 ; // 60 * 15; // 4.5 minutes delay

// Winners List Check
//-- Check for updates to winners every few minutes
settings.winnersDelay = 1000 * 60 * 5; // 5 Minutes

// Buoy List Check
//-- Check the buoy configuration
settings.buoysDelay = 1000 * 60 * 2; // 4 Minute
settings.buoysLoaded = false;

// News Check
//-- Check for the latest news
settings.newsDelay = 1000 * 60 * 1; // 1 Minute
settings.newsLoaded = false;

// Racers List Check
//-- Get the list of racers
settings.racersDelay = 1000 * 60 * 3 // 3 minute
settings.racersLoaded = false;

// Telemetry Data 
//-- Get Position and speed and stuff
settings.telemetryDelay = 1000 * 60 * 1 // 1 minute
settings.telemetryLoaded = false;

// Draw the route the boat is going to take
settings.drawRoute = false;
// Draw Trails behind the boats
settings.drawTrails = true;
// Follow the boats
settings.followBoats = false;
// Distance Unit to use for display
settings.DistanceUnits = { Miles:"m", Kilometers:"k" };
settings.UseDistanceUnits = settings.DistanceUnits.Kilometers;

settings.lastTrack = -1;
// The Fake Data Goes Here
data.buoys = new Array();
/**
new Array (
	{
		name:"Start Pair",
		b1:1010,
		b2:1011
	},
	{
		name:"End Pair",
		b1:1100,
		b2:1101
	}
);
**/
// Data Holder Classes
function SrtUnit( pName, pIcon, pNode, pIsVehicle, pColorR, pColorG, pColorB )
{
	this.name = pName;
	this.icon = pIcon;
	this.node = pNode;
	this.isVehicle = pIsVehicle;
	this.colorRGB = { r: pColorR, g: pColorG, b: pColorB },
	this.lastTimestamp = 0;
	this.largestTimestamp = 0;
	this.forcePosition = false;
	this.forceLat = 0;
	this.forceLon = 0;
	this.tracks = new Array();
	var trailLayer = new VEShapeLayer();
	this.layer = trailLayer;
	oMap.AddShapeLayer( trailLayer ); 
}
function GpsTrack( pLat, pLon, pTimestamp, pSpeed, pHeading )
{
	this.lat = pLat;
	this.lon = pLon;
	this.timestamp = pTimestamp;
	this.timeString = new Date( pTimestamp );
	this.heading = pHeading;
	this.speed = pSpeed;
}
data.racers = new Array();
/**
data.racers = new Array(
	new SrtUnit( "B1", "images/green-ball.gif", 1010, false, 0, 255, 0 ),
	new SrtUnit( "B2", "images/green-ball.gif", 1011, false, 0, 255, 0 ),
	new SrtUnit( "B3", "images/green-ball.gif", 1010, false, 0, 255, 0 ),
	new SrtUnit( "B4", "images/green-ball.gif", 1010, false, 0, 255, 0 ),
	new SrtUnit( "R1", "images/red-ball.gif", 1010, true, 255, 0, 0 ),
	new SrtUnit( "R2", "images/red-ball.gif", 1010, true, 255, 0, 0 )
);
for ( var tx = 1; tx < data.racers.length; tx++ )
{
	data.racers[tx-1].tracks = generateTracks(simData.simStartLat,simData.simStartLon, new Date().valueOf() , new Date().valueOf() + ( 1000 * 60 * simData.simMinutes ) );
}
***/
function StartClockTimer()
{
	// update clock each second
	settings.clockTimer = setInterval( updateClock, 1000 );
}
function updateClock()
{
	// Calculate Various Times
	var localtime = new Date().valueOf();
	var timeZoneOffsetMs = new Date().getTimezoneOffset() * 1000 * 60;
	// SetStatus( 'Timezome Offset = ' + new Date().getTimezoneOffset() );

	var UtcTime = localtime + timeZoneOffsetMs;
	var targettime = UtcTime - settings.eventDelayTimeMs;
	var racetime = targettime + settings.raceOffsetMs;
	// Race time :0


	settings.useThisRaceTime = racetime;

	// Display the local time
	goGetElementById( "ltime" ).innerHTML = new Date( localtime ).toLocaleString();

	// Display the race time
	goGetElementById( "rtime" ).innerHTML = new Date( racetime ).toLocaleString();
}

function SetStatus( pMessage )
{
//	blocking( 'status', true );
	try
	{
		if ( pMessage )
		{
			var elem = goGetElementById( 'status' );
			elem.innerHTML = pMessage;
		}
	} catch( error )
	{
	}
}

function ClearStatus()
{
//	blocking( 'status', false );
	try
	{
		var elem = goGetElementById( 'status' );
		elem.innerHTML = "";
	} catch( error )
	{
	}
}

// AJAX Stuff
//   return XMLHttp object or null
function getAJAXObject()
{
	var req;
	try 
	{
		if ( window.XMLHttpRequest )
		{
			req = new XMLHttpRequest();
		} else if ( window.ActiveXObject ) 
		{
			req = new ActiveXObject( "Microsoft.XMLHTTP" );
		}
	} catch( error )
	{
		req = null;
	}
	return req;
}

function generateVehicle( )
{
	var oV;
	var names = [ 'Liberty', 'Victory', 'Aquaholic', 'Bite Me', 'Endless Summer', 'Seahorse', 'Footloose', 'Silver Lining', 'Miss Behavin', 'Moondance', ' Second Wind', 'Endless Summer', 'Happy Ours', 'Time Out', 'Fish Tales', 'Island Time', 'Sea Ya', 'Lazy Daze', 'Luna Sea', 'Serenity', ' Irish Eyes', 'Island Time', 'Sea Spirit', 'Obsession', 'Time Out', 'Reel Time', 'Escapade', 'Southern Comfort', 'Serendipity', ' Misty', 'Flying Cloud'] ;
	

}
function RenderBuoys( pTimeMs )
{
	for ( var rbx = 1; rbx <= data.buoys.length; rbx++ )
	{
		// Get Vehicle Indexes for Each Buoy
		var b1_index = FindRacerNode( data.buoys[rbx-1].b1 );
		var b2_index = FindRacerNode( data.buoys[rbx-1].b2 );

		// Get Vehicle Position for Each Buoy
		if ( b1_index != -1  && b2_index != -1 )
		{
			var GpsB1 = GetVehiclePositionAtTime( data.racers[b1_index], pTimeMs );
			var GpsB2 = GetVehiclePositionAtTime( data.racers[b2_index], pTimeMs );
			if ( null != GpsB1 && null != GpsB2 )
			{
				var buoyLinePts = new Array(
					new VELatLong( GpsB1.lat, GpsB1.lon ),
					new VELatLong( GpsB2.lat, GpsB2.lon )
				);
				var line = new VEShape( VEShapeType.Polyline, buoyLinePts );
				line.SetLineColor( new VEColor( data.racers[b1_index].colorRGB.r, data.racers[b1_index].colorRGB.g, data.racers[b1_index].colorRGB.b, 0.8 ) );
				line.HideIcon();
				data.buoys[rbx-1].layer.DeleteAllShapes();
				data.buoys[rbx-1].layer.AddShape( line );
			}
	}	}
}
 
function RenderTrails( pVehicle, pTimeMs )
{
	// Returns an array of the trails rendered
	var AllTailPoints = new Array();

	var tFromTimeMs;
	var tToTimeMs;
	
	var tSegmentLengthMs = ( settings.trailLengthMs / settings.trailSegments );

	// Adjust Trail Length if we don't have enough data
	if ( pTimeMs - settings.trailLengthMs < pVehicle.tracks[0].timestamp )
	{
		var tTrailLengthMs = pTimeMs - pVehicle.tracks[0].timestamp;
		tSegmentLengthMs = ( tTrailLengthMs / settings.trailSegments );
	}

	// Clear the previous trails
	pVehicle.layer.DeleteAllShapes();

	// Draw the next set of trails, fading to zero
	for( var x = 1; x < settings.trailSegments; x++ )
	{
		// Calculate Tail Segment Start and End Times
		tFromTimeMs = pTimeMs - tSegmentLengthMs * ( x - 1 );
		tToTimeMs = pTimeMs - tSegmentLengthMs * x;

		// Calculate Positions at those times
		var gpsFrom = GetVehiclePositionAtTime( pVehicle, tFromTimeMs );
		var gpsTo = GetVehiclePositionAtTime( pVehicle, tToTimeMs );

		// Render those times if we were able to calculate them
		if ( gpsFrom != null && gpsTo != null )
		{
			// Add the new line to the layer
			var points = new Array(
				new VELatLong( gpsFrom.lat, gpsFrom.lon ),
				new VELatLong( gpsTo.lat, gpsTo.lon )
			);
			AllTailPoints = AllTailPoints.concat( points );
			var line = new VEShape( VEShapeType.Polyline, points );
			line.HideIcon();

			// Calculate Alpha shading to fade the trail over time
			var alpha = ( settings.trailSegments - x - 1 ) / settings.trailSegments;
			line.SetLineColor( new VEColor( pVehicle.colorRGB.r, pVehicle.colorRGB.g, pVehicle.colorRGB.b, alpha ) );
			pVehicle.layer.AddShape( line );
		}

	}
	if ( AllTailPoints.length > 0 )
	{
		return AllTailPoints;
	} else {
		return null;
	}
}
           
function GetMSVEMap()
{
	// { debugger; }
	try 
	{
		oMap = new VEMap('map');
	} catch( error )
	{
		if ( settings.mapRetries )
		{
			settings.mapRetries += 1;
		} else {
			settings.mapRetries = 1;
		}
		SetStatus( 'Map Load Failed, trying again...' );
		setTimeout( GetMSVEMap, 500 );
		return null;
	}
	oMap.onLoadMap = mapReady;
	oMap.LoadMap( new VELatLong(simData.simStartLat,simData.simStartLon ) ,simData.simStartZoom, "r", false, VEMapMode.Mode2D, true );
}   
function mapReady()
{
	
	// Add the updating function to the zoom event when you init the map
	oMap.AttachEvent("onendzoom", updateImageOffset);

	data.racers.push(
		new SrtUnit( "Orange Leeward Mark", "images/orange-ball.gif", 927, false, 0, 255, 0 )
	);
	data.racers[0].tracks.push( new GpsTrack( 43.57316667, 79.3805, new Date( 2007, 09,13,0,0,0).valueOf() , 0, 0 ) );
	data.racers[0].tracks.push( new GpsTrack( 43.57316667, 79.3805, new Date( 2007, 09,16,0,0,0).valueOf() , 0, 0 ) );
	// Lets us know the VE Map object is ready to be used
	startAnimationTimer();
	MapResize();
	if ( settings.followBoats )
	{
		oMap.ShowMiniMap( 10, 10 );
	} else {
		oMap.ShowMiniMap( );
	}
	oMap.SetScaleBarDistanceUnit( settings.UseDistanceUnits );
	
	/**
	// Add Layers for Each Buoy 
	for ( var x = 1; x <= data.buoys.length; x++ )
	{
		var newLayer = new VEShapeLayer();
		oMap.AddShapeLayer( newLayer );
		data.buoys[x-1].layer = newLayer;
	}
	// Add Layers for Each Vehicles Trail Line
	for( var x = 1; x <= data.racers.length; x++ )
	{
		{ debugger; }
		var newLayer = new VEShapeLayer();
		oMap.AddShapeLayer( newLayer );
		data.racers[x-1].layer = newLayer;
	}
	**/
	if ( settings.drawRoute )
	{	
		RenderAllVehicleRoutes( data.racers );
	}
	SetStatus( 'Initializing Data Connection' );
	InitAJAX();
}
function startAnimationTimer()
{
	// alert( 'Starting Animation Timer ' );
	SetStatus( 'Starting Animation...' );
	settings.animTimer = setInterval( RenderWorld, settings.animInterval );
}
function stopAnimationTimer()
{
	SetStatus( 'Stopping Animation...' );
	clearInterval( settings.animTimer );
}

function RenderWorld()
{
	try
	{
		if ( ! data.dataLoaded )
		{
			SetStatus( 'Waiting for Data...' );
			return null;
		}
		if ( settings.replayMode )
		{
		}
		var tVal = GetAnimationTime();
		// var tVal = GetUtcNow();
		// SetStatus( 'Rendering World at ' + new Date( tVal) );
	//	if ( data.buoyUpdated )
	//	{
			RenderBuoys( tVal );
	//	};
	//	if ( data.courseUpdate )
	//	{
	//		RenderCourse();
	//	};
		RenderVehicles( tVal );
	} catch( error )
	{
		console.log( 'Error in RenderWorld ' );
	}
}
function RenderVehicles( pTimeMs )
{
	var layer0 = oMap.GetShapeLayerByIndex(0);
	layer0.DeleteAllShapes();
	RenderVehiclesAtTime( pTimeMs );
}
function GetAnimationTime()
{
	// Return time in milliseconds
	//return new Date().valueOf();
	return settings.useThisRaceTime ;
	// var UTC_now = new Date().valueOf() + new Date().getTimezoneOffset() * 60 * 1000;
	// return UTC_now;
}

function RenderVehiclesAtTime( pTimeMs )
{
	//oMap.ShowMessage( new Date( pTimeMs ).toString() );
	var AllVehicles = new Array();
	for( var x = 1; x <= data.racers.length; x++ )
	{
		var pRacer = data.racers[x-1];
		var gps = GetVehiclePositionAtTime( pRacer, pTimeMs );
		if ( null != gps )
		{
			AllVehicles.push(  new VELatLong( gps.lat, gps.lon ) );
			// Add the Icon to the map
			var pin = oMap.AddPushpin( new VELatLong( gps.lat, gps.lon ) );
			// Set the bold title for mouse over
			pin.SetTitle( pRacer.name );
			if ( pRacer.isVehicle )
			{
				var tHTML = '<b>Heading:</b> ' + gps.heading + '<br />';
				tHTML += '<b>Speed:</b> ' + gps.speed + '<br />';
				pin.SetDescription( tHTML );
				var imgHeading = mod( Math.round( ( 360 + -1 * gps.heading ) / 10 ) * 10, 360 );
				// var imgUrl = 'http://tracker.ionearth.com/images/red_triangle_' + imgHeading + '.png';
				var imgUrl = pRacer.icon + imgHeading + '.png';
				pin.SetCustomIcon( imgUrl );
				var headingTextEl = goGetElementById( 'node' + pRacer.node + 'hd');
				headingTextEl.innerHTML = gps.heading;

				var nodeSpeedEl = goGetElementById( 'node' + pRacer.node +  'speed' );
				nodeSpeedEl.innerHTML = gps.speed;

				// document['node' + pRacer.node + 'heading'].src = imgURL;

				
			} else 
			{
				// Set the Custom Icon for this vehicle
				pin.SetCustomIcon( pRacer.icon );
			}
			// oMap.SetCenter( new VELatLong( gps.lat, gps.lon ) );

			// Draw trails if it's a vehicle
			if ( settings.drawTrails )
			{
				if ( pRacer.isVehicle )
				{
					var tailPoints = RenderTrails( pRacer, pTimeMs );
					if ( null != tailPoints )
					{
						AllVehicles = AllVehicles.concat( tailPoints );
					}
				}
				
				// oMap.SetCenter( new VELatLong( gps.lat, gps.lon ) );
			}
		}
	}
	if ( settings.followBoats )
	{
		try
		{
			oMap.SetMapView( AllVehicles );
		} catch( error )
		{
			// do nothing, catch the error so it doesn't stop the animation
		}
	}
}
function InitAJAX()
{
	setTimeout( InitAjaxWinners, 2000 );
	setTimeout( InitAjaxBuoys, 1000 );
	setTimeout( InitAjaxNews, 1000 );
	// setTimeout( InitAjaxRacers, 1500 );
	// setTimeout( InitAjaxTelemetry, 3000 );
}
function InitAjaxNews()
{
        settings.newsAJAX = getAJAXObject();
        if ( null == settings.newsAJAX )
        {
                alert( 'Unable to connect to server<br />Please reload this web page' );
        }
        settings.newsAJAX.onreadystatechange = function()
        {
                if ( 4 == settings.newsAJAX.readyState )
                {
                        SetStatus( 'Got news Update' );
                        if ( 200 == settings.newsAJAX.status )
                        {
				try
				{
					settings.newstring = settings.newsAJAX.responseText;
					var newsElement = goGetElementById( 'race_news' );
					newsElement.innerHTML = settings.newstring;
					setTimeout( ClearStatus, 2000 );
				} finally {
					setTimeout( InitAjaxNews, settings.newsDelay );
				}
                        } else {
                                setTimeout( InitAjaxNews, settings.newsDelay );
                        }
                }
        }
        SetStatus( 'Checking For Latest News ' );
        var resultURL = "data/news.txt?" + new Date().valueOf();
        settings.newsAJAX.open( "GET", resultURL, true );
        settings.newsAJAX.send( null );
	
}
function InitAjaxRacers()
{
        settings.racersAJAX = getAJAXObject();
        if ( null == settings.racersAJAX )
        {
                alert( 'Unable to connect to server<br />Please reload this web page' );
        }
        settings.racersAJAX.onreadystatechange = function()
        {
                if ( 4 == settings.racersAJAX.readyState )
                {
                        SetStatus( 'Got racers Update' );
                        if ( 200 == settings.racersAJAX.status )
                        {
                                settings.racerstring = settings.racersAJAX.responseText;
                                updateRacers();
				if ( ! settings.telemetryLoaded )
				{
					setTimeout( InitAjaxTelemetry, 300 );
				}
                        } else {
                                setTimeout( InitAjaxRacers, settings.racersDelay );
                        }
                }
        }
        SetStatus( 'Checking Buoy Configuration ' );
        var resultURL = "data/racers.txt?" + new Date().valueOf();
        settings.racersAJAX.open( "GET", resultURL, true );
        settings.racersAJAX.send( null );
}
function FindRacerNode( pNodeId )
{
	var defaultValue = -1;
	try
	{
		for ( var rx = 1; rx < data.racers.length; rx++ )
		{
			if ( data.racers[rx-1].node == pNodeId )
			{
				return rx-1;
			}
		}
	} catch( error )
	{
		// Thou shalt not pass!
	}
	return defaultValue;
}
function updateRacers()
{
	SetStatus( 'Loading Racers... ' );
        // Parse the string that makes up the file to populate the racers object
        if ( settings.racerstring )
        {
                var racersList = settings.racerstring.split( "\n" );
                for( var bx = 1; bx <= racersList.length; bx++ )
                {
                        try
                        {
                                var racerBits = racersList[bx-1].split(",");
                                var tnode = racerBits[0];
                                var tname = racerBits[1];
                                var tisVehicle = ( racerBits[2] == "yes" ) ; // ? true : false;
				var ticon = racerBits[3];
				var tRgbR = racerBits[4];
				var tRgbG = racerBits[5];
				var tRgbB = racerBits[6];
								
                                if ( tnode != null && null != tname && null != tisVehicle && null != ticon && null != tRgbR && null != tRgbG && null != tRgbB )
                                {
					// We have good data now lets make sure we don't overwrite anything
					var existingIndex = FindRacerNode( tnode );
					if ( existingIndex < 0 )
					{
						var tLayer = new VEShapeLayer();	
						oMap.AddShapeLayer( tLayer ); // data.racers[data.racers.length-1].layer );
						var newVehicle = new SrtUnit( tname, ticon, tnode, tisVehicle, tRgbR, tRgbG, tRgbB );
						if ( racerBits.length >= 8 )
						{
							newVehicle.forcePosition = true;
							newVehicle.forceLat = racerBits[8];
							newVehicle.forceLon = racerBits[9];
						}
						data.racers.push( newVehicle );
						/**
						data.racers.push(
						 {
							node:tnode,
							name:tname,
							isVehicle:tisVehicle,
							icon:ticon,
							colorRGB: { r:tRgbR, g:tRgbG, b:tRgbB },
							layer: tLayer,
							tracks: new Array()
						 });
						**/
                                        } else {
						// Replace the existing data, preserve nodeID (obviously) and tracks
						data.racers[existingIndex].name = tname,
						data.racers[existingIndex].isVehicle = tisVehicle,
						data.racers[existingIndex].icon = ticon,
						data.racers[existingIndex].colorRGB = { r:tRgbR, g:tRgbG, b:tRgbB };
					}
                                }
                        } catch( error )
                        {
                                // Something bad happened
                                //--- We'll think about that later
                        }
                }
        }
	SetStatus( 'Racers Loaded' );
	settings.racersLoaded = true;
        setTimeout( InitAjaxRacers, settings.racersDelay );
}
function InitAjaxTelemetry()
{
        settings.telemetryAJAX = getAJAXObject();
        if ( null == settings.telemetryAJAX )
        {
                alert( 'Unable to connect to server<br />Please reload this web page' );
        }
        settings.telemetryAJAX.onreadystatechange = function()
        {
		try {
			var rState = settings.telemetryAJAX.readyState;
			switch( rState )
			{
				case 0:
				case 1:
				case 2:
				case 3:
					// Do nothing
					return;
					break;
				case 4:
					
					SetStatus( 'Got Telemetry Update' );
					var rStatus = settings.telemetryAJAX.status;
					if ( 200 == rStatus )
					{
						settings.telemetryString = settings.telemetryAJAX.responseText;
						updateTelemetry();
						// ClearStatus();
					} else {
						setTimeout( InitAjaxTelemetry, settings.telemetryDelay );
					}
				default:
					break;
			}
		} catch( error )
		{
			SetStatus( 'Error in Telemetry handler<br />Error: ' + error.description );
			setTimeout( InitAjaxTelemetry, settings.telemetryDelay );
		}
        }
        SetStatus( 'Downloading Telemetry Data...' );
        var resultURL = "data/telemetry_0.txt?" + new Date().valueOf();
        settings.telemetryAJAX.open( "GET", resultURL, true );
        settings.telemetryAJAX.send( null );

}
function updateTelemetry()
{
	SetStatus( 'Loading Position Update' );
	var trackCount = 0;

        // Parse the string that makes up the file to populate the racers object
        if ( settings.telemetryString )
        {
                var telemetryLines = settings.telemetryString.split( "\n" );
                for( var bx = 1; bx <= telemetryLines.length; bx++ )
                {
			if ( bx <= settings.lastTrack )
			{
				continue;
			}		
                        try
                        {
                                var telemetryBits = telemetryLines[bx-1].split(",");
                                var tid = telemetryBits[0];
                                var tnode = telemetryBits[1];
				/**
                                // var ttimestamp = new Date( telemetryBits[2] ).valueOf();
                                var tlat = parseInt(telemetryBits[3]);
                                var tlon = parseInt(telemetryBits[4]);
                                var tspeed = parseInt(telemetryBits[5]);
                                var theading = parseInt(telemetryBits[6]);
				**/
                                var tlat = parseFloat(telemetryBits[2]);
                                var tlon = parseFloat(telemetryBits[3]);
                                var tspeed = parseFloat(telemetryBits[4]);
                                var theading = parseInt(telemetryBits[5]);
				var tyear = parseInt(telemetryBits[6]);
				var tmonth = parseInt(telemetryBits[7]);
				var tdate = parseInt(telemetryBits[8]);
				var thour = parseInt(telemetryBits[9]);
				var tmin = parseInt(telemetryBits[10]);
				var tsec = parseInt(telemetryBits[11]);
				var tmsec = parseInt(telemetryBits[12]);
				// var ttimestamp = new Date().UTC( tyear, tmonth -1, tdate, thour, tmin, tsec, tmsec );
				var ttimestamp = new Date( tyear, tmonth -1, tdate, thour, tmin, tsec, tmsec ).valueOf();
				// var ttimestamp = tDate.valueOf() ; // - settings.raceOffsetMs ;
				var tDateString = new Date( ttimestamp );
				// tspeed = tspeed / 10.0;


                                if ( null != tid && null != tnode && null != ttimestamp && null != tlat && null != tlon && null != tspeed && null != theading )
                                {
					trackCount += 1;
					settings.lastTrack = bx;

					// { debugger; }
                                        // We have good data now lets make sure we don't overwrite anything
                                        var existingIndex = FindRacerNode( tnode );
                                        if ( existingIndex == -1 )
                                        {
						// alert( "Can't find the vehicle with Node '" + tnode + "'" );

						/** Ignore - we don't have this vehicle 
                                                data.racers[data.racers] = {
                                                        node:tnode,
                                                        name:tname,
                                                        isVehicle:tisVehicle,
                                                        icon:ticon
                                                        colorRGB: { r:tRgbR, g:tRgbG, b:tRgbB },
                                                        tracks: new Array()
                                                };
						**/
                                        } else {
                                                // Replace the existing data, preserve nodeID (obviously) and tracks
						var tTrackLength = data.racers[existingIndex].tracks.length;
						data.racers[existingIndex].lastTimestamp = ttimestamp;
						if ( ttimestamp > data.racers[existingIndex].largestTimestamp )
							data.racers[existingIndex].largestTimestamp = ttimestamp;

						// Append the current "track" to the end of the array
						var newGpsTrack = new GpsTrack( tlat, tlon, ttimestamp, tspeed, theading );
						data.racers[existingIndex].tracks.push( newGpsTrack );

                                        }
                                }
                        } catch( error )
                        {
                                // Something bad happened
                                //--- We'll think about that later
                        }
                }
        }
	if ( ! data.dataLoaded )
	{
		data.dataLoaded = ( trackCount > 0 );
	}
	SetStatus( 'Position Update Complete' );
	settings.telemetryLoaded = true;
	setTimeout( ClearStatus, 3000 );
        setTimeout( InitAjaxTelemetry, settings.telemetryDelay );
}
function InitAjaxBuoys()
{
	settings.buoysAJAX = getAJAXObject();
	if ( null == settings.buoysAJAX )
	{
		alert( 'Unable to connect to server<br />Please reload this web page' );
	}
	settings.buoysAJAX.onreadystatechange = function()
	{
		switch( settings.buoysAJAX.readyState )
		{
			case 0:
			case 1:
			case 2:
			case 3:
				// Ignore these
				return;
				break;
			case 4:
				SetStatus( 'Got Buoys Update' );
				if ( 200 == settings.buoysAJAX.status )
				{ 
					settings.buoyString = settings.buoysAJAX.responseText;
					updateBuoys();
	
					// BuoyData is loaded, now load the racer data
					if ( ! settings.racersLoaded )
					{
						setTimeout( InitAjaxRacers, 500 );
					}
				}
				// Data is loaded, set timer to get buoy data again
				setTimeout( InitAjaxBuoys, settings.buoysDelay );
		}
	}
	SetStatus( 'Checking Buoy Configuration ' );
	var resultURL = "data/buoy_data.txt?" + new Date().valueOf();
	settings.buoysAJAX.open( "GET", resultURL, true );
	settings.buoysAJAX.send( null );
}
function updateBuoys()
{
	// Parse the string that makes up the file to populate the buoys object
	if ( settings.buoyString )
	{
		if ( data.buoys.length > 0 )
		{
			for ( var bx = 1; bx <= data.buoys.length; bx++ )
			{
				oMap.DeleteShapeLayer( data.buoys[bx-1].layer );
			}
		}
		data.buoys = new Array(); 
		var buoyList = settings.buoyString.split( "\n" );
		for( var bx = 1; bx <= buoyList.length; bx++ )
		{
			try
			{
				var buoyBits = buoyList[bx-1].split(",");
				var tname = buoyBits[0];
				var tb1 = buoyBits[1];
				var tb2 = buoyBits[2];
				var tlayer = new VEShapeLayer();
				if ( tname && tb1 && tb2 )
				{
					data.buoys.push(
					{
						name:tname,
						b1:tb1,
						b2:tb2,
						layer: tlayer
					});
					oMap.AddShapeLayer( tlayer );
				}
			} catch( error )
			{
				// Something bad happened 
				//--- We'll think about that later
			}
		}
	}
	setTimeout( InitAjaxBuoys, settings.buoysDelay );
	settings.buoysLoaded = true;
}
function InitAjaxWinners()
{
	settings.winnersAJAX = getAJAXObject();
	if ( null == settings.winnersAJAX )
	{
		alert( 'Unable to connect to server<br />Please reload this web page' );
	}
	settings.winnersAJAX.onreadystatechange = function()
	{
		if ( 4 == settings.winnersAJAX.readyState )
		{
			SetStatus( 'Got Winners Update' );
			if ( 200 == settings.winnersAJAX.status )
			{ 
				settings.winnerString = settings.winnersAJAX.responseText;
				updateWinners();
			} else {
				setTimeout( InitAjaxWinners, settings.winnersDelay );
			}
		}
	}
	SetStatus( 'Checking Winners Update' );
	var resultURL = "data/winners.txt?" + new Date().valueOf();
	settings.winnersAJAX.open( "GET", resultURL, true );
	settings.winnersAJAX.send( null );
}
function updateWinners()
{
	if ( settings.winnerString )
	{
		var winnersList = settings.winnerString.split( "\n" );
		for ( var wx = 1; wx <= winnersList.length; wx++ )
		{
			try 
			{
				var tResult = winnersList[wx-1].split( ":" );
				if ( "" != tResult )
				{
					var tRace = tResult[0];
					var tImageIndex = tResult[1];
					var tImageURL = Enum.winnerImages[tImageIndex];
					tImageURL = "url(" + tImageURL + ")";
					var raceElement = goGetElementById( 'race' + tRace );
					raceElement.style.backgroundImage = tImageURL;
				}
			} catch( error )
			{
				SetStatus( 'Error in updateWinners<br />Error ' + error.description );
			}
		}
	}
	setTimeout( InitAjaxWinners, settings.winnersDelay );
}
function InitMap()
{
	GetMSVEMap();
}
function Init()
{
	StartClockTimer();
	SetStatus( 'Initializeing Map...' );
	InitMap();

}
function goGetElementById( pId )
{
	if ( document.layers )
	{
		return document.layers[pId];
	} else if ( document.all )
	{
		return document.all[pId];
	} else if ( document.getElementById )
	{
		return document.getElementById( pId );
	}
}
function blocking(nr, showBlockBool )
{
	if (document.layers)
	{
		// current = (document.layers[nr].display == 'none') ? 'block' : 'none';
		current = (showBlockBool) ? 'block' : 'none';
		document.layers[nr].display = current;
		visible = (showBlockBool) ? 'show' : 'hide';
		document.layers[nr].visibility = visible;
	}
	else if (document.all)
	{
		// current = (document.layers[nr].display == 'none') ? 'block' : 'none';
		current = (showBlockBool) ? 'block' : 'none';
		document.all[nr].style.display = current;
		visible = (showBlockBool) ? 'show' : 'hide';
		document.all[nr].style.visibility = visible;
	}
	else if (document.getElementById)
	{
		// current = (document.layers[nr].display == 'none') ? 'block' : 'none';
		vista = (showBlockBool) ? 'block' : 'none';
		document.getElementById(nr).style.display = vista;
		visible = (showBlockBool) ? 'show' : 'hide';
		document.getElementById(nr).style.visibility = visible;
	}
}
function MapResize()
{
	if(oMap!=null)
	{
		if(typeof(window.innerWidth)=='number')
		{
			// alert( '1' );
			// Netscape
			// Make map full display size
			oMap.Resize(window.innerWidth - 250,window.innerHeight);
			//var oMy = document.getElementById( 'map' );
			//oMap.Resize(oMy.innerWidth,oMy.innerHeight);
		}
		else if(document.documentElement&&(document.documentElement.clientWidth||document.documentElement.clientHeight))
		{
			// IE 7
			// oMap.Resize(document.documentElement.clientWidth ,document.documentElement.clientHeight);
			oMap.Resize(document.documentElement.clientWidth - 250,document.documentElement.clientHeight);
		}
	}
}



function generateTracks( StartLat, StartLon, StartTime, EndTime)
{
	
	var radius = Math.random() * ( simData.simRadiusMax - simData.simRadiusMin ) + simData.simRadiusMin;
	var startAngle = 0; // Math.random()*360;
	
	var newCenterLon = StartLon + 0.5 * radius * Math.cos( startAngle / 180 * Math.PI  );
	var newCenterLat = StartLat + 0.5 * radius * Math.sin( startAngle / 180 * Math.PI );
	
	var pointCount = Math.round(Math.random() * 16 + 16);
	var angleDelta = 180 / pointCount;
	var revAngle = startAngle + 180;
	var timeDelta = ( EndTime - StartTime ) / pointCount ;
	
	var tracks = new Array();
	var theAngle = revAngle;
	var theTime = StartTime;
	for ( var x = 0; x < pointCount; x++ )
	{
		var t = {};
		t.lon = newCenterLon + radius * Math.cos( theAngle  / 180 * Math.PI );
		t.lat = newCenterLat + radius * Math.sin( theAngle  / 180 * Math.PI );
		t.timestamp = theTime;

		t.heading = theAngle ;
		t.speed = 99;
		tracks[x] = t;
		theAngle += angleDelta;
		theTime += timeDelta;
	}
	return tracks;
}

function DisplayAllVehicles( pRacerArray )
{
	var vehicleCount = pRacerArray.length;
	document.writeln( 'There are ' + vehicleCount + ' Vehicles <br />' );
	for ( var v = 0; v < vehicleCount; v++ )
	{
		if ( 1 == 0 ) { debugger; }
		var Racer = pRacerArray[v];
		document.writeln( 'Vehicle ' + (v + 1) + ' <br />' );
		document.writeln( '<ul>' );
		document.writeln( '  <li>Name: ' + Racer.name + '</li>' );
		document.writeln( '  <li>Icon ' + Racer.icon + '</li>' );
		document.writeln( '  <li>Tracks...<br />' );
		if ( Racer.tracks && Racer.tracks.length > 0 )
		{
			document.writeln( '  <ul>' );
			for ( var t = 1; t <= Racer.tracks.length; t++ )
			{
				var track = Racer.tracks[t-1];
				document.writeln( '    Track ' + t );
				document.writeln( '    <ul>' );
				document.writeln( '      <li>Lat ' + track.lat + '</li>' );
				document.writeln( '      <li>Lon ' + track.lon + '</li>' );
				document.writeln( '      <li>Time ' + new Date( track.timestamp ) + '</li>' );
				document.writeln( '    </ul>' );
			}
			document.writeln( '  </ul>' );
		}
		document.writeln( '  </li>' );
		document.writeln( '</ul>' );
	}
}

function RenderAllVehicleRoutes( pRacerArray )
{
	var vehicleCount = pRacerArray.length;
	// document.writeln( 'There are ' + vehicleCount + ' Vehicles <br />' );
	for ( var v = 0; v < vehicleCount; v++ )
	{
		if ( 1 == 0 ) { debugger; }
		var Racer = pRacerArray[v];
		/**
		document.writeln( 'Vehicle ' + (v + 1) + ' <br />' );
		document.writeln( '<ul>' );
		document.writeln( '  <li>Name: ' + Racer.name + '</li>' );
		document.writeln( '  <li>Icon ' + Racer.icon + '</li>' );
		document.writeln( '  <li>Tracks...<br />' );
		**/
		var courseLayer = new VEShapeLayer();
		oMap.AddShapeLayer(courseLayer);
		if ( Racer.tracks && Racer.tracks.length > 0 )
		{
			//document.writeln( '  <ul>' );
			for ( var t = 1; t < Racer.tracks.length; t++ )
			{
				var track = Racer.tracks[t-1];
				var points = new Array(
					new VELatLong( Racer.tracks[t-1].lat, Racer.tracks[t-1].lon ),
					new VELatLong( Racer.tracks[t].lat, Racer.tracks[t].lon )
				);
				var line = new VEShape( VEShapeType.Polyline, points );
				line.HideIcon();
				var alpha = ( Racer.tracks.length - t - 1 ) / Racer.tracks.length;
				
				line.SetLineColor( new VEColor( 128, 128, 255, alpha ) );
				courseLayer.AddShape( line );
				/**
				document.writeln( '    Track ' + t );
				document.writeln( '    <ul>' );
				document.writeln( '      <li>Lat ' + track.lat + '</li>' );
				document.writeln( '      <li>Lon ' + track.lon + '</li>' );
				document.writeln( '      <li>Time ' + new Date( track.timestamp ) + '</li>' );
				document.writeln( '    </ul>' );
				**/
			}
			//document.writeln( '  </ul>' );
		}
		//document.writeln( '  </li>' );
		//document.writeln( '</ul>' );
	}
}

function DisplayVehiclePositions( pVehicle, StartTimeMs, EndTimeMs, IncrementMs )
{
	if ( StartTimeMs > EndTimeMs )
	{
		var temp = StartTimeMs;
		StartTimeMs = EndTimeMs;
		EndTimeMs = temp;
	}
	document.writeln( 'Computing from <br />' );
	document.writeln( '&nbsp;&nbsp;Start Time: ' + new Date( StartTimeMs ) + ' to <br/>' );
	document.writeln( '&nbsp;&nbsp;End Time: ' + new Date( EndTimeMs ) + '<br/>' );
	document.writeln( '&nbsp;&nbsp;In ' + IncrementMs / 1000 + ' Second Intervals <br />' );
	document.writeln( '<table cellspacing="0" cellpadding="0" border="1">' );
	document.writeln( '<tr>' );
	document.writeln( '  <th colspan="4">' + pVehicle.name + '</th>' );
	document.writeln( '</tr>' );
	document.writeln( '<tr>' );
	document.writeln( '  <th>#</th>' );
	document.writeln( '  <th>Time</th>' );
	document.writeln( '  <th>Lat</th>' );
	document.writeln( '  <th>Lon</th>' );
	document.writeln( '</tr>' );
	
	try
	{
		
		var T;
		var count = 0;
		for ( T = StartTimeMs; T <= EndTimeMs; T += IncrementMs )
		{
			count += 1;
			var gps = GetVehiclePositionAtTime( pVehicle, T );
			document.writeln( '<tr>' );
			document.writeln( '  <td>' + count + '</td>' );
			document.writeln( '  <td>' + new Date( T ) + '</td>' );
			if ( null != gps )
			{
				document.writeln( '  <td>' + gps.lat + '</td>' );
				document.writeln( '  <td>' + gps.lon + '</td>' );
			}
			document.writeln( '</tr>' );
		}
	} finally {
		document.writeln( '</table>' );
	}
}


function GetVehiclePositionAtTime( pVehicle, pTimeMs )
{
	//document.write( '<hr />Computing Vehicle ' + pVehicle.name + '<br />' );
	//document.write( 'Position at time ' + new Date( pTimeMs ) + ' <hr />' );
	// Change system to return last known position if available
	if ( ! pVehicle )
	{
		//document.writeln( '<h4>Debug: Vehicle Object does not have any tracks </h4> ' );
		return null;
	}
	
	var pTimeUTCMs = pTimeMs; // + settings.raceOffsetMs;
	var debugTime = new Date( pTimeUTCMs );

	if ( pVehicle.forcePosition )
			return { lat: pVehicle.forceLat, lon: pVehicle.forceLon, heading: 0, speed: 0, timestamp: pTimeUTCMs, lastknown: false };
	

	var TrackLength = -1;
	var GpsPos = null;
	var lastKnown = false;
	try
	{
		TrackLength = pVehicle.tracks.length;
	} catch( error ) {
		{ debugger; }
		document.writeln( '<h4>Debug: Vehicle Tracks property is not an array of tracks </h4> ' );
		return null;
	}
	// document.writeln( 'Vehicle Track Length = ' + TrackLength + ' <br />' );
	if ( TrackLength < 2 ) 
	{
		//document.writeln( '<h4>Debug: Vehicle Object has less than two tracks </h4> ' );
		
		return null;
	}
	var earliestTimeUTC = pVehicle.tracks[0].timestamp;
	var latestTimeUTC = pVehicle.tracks[TrackLength-1].timestamp ;
	var debugEarly = new Date( earliestTimeUTC );
	var debugLate = new Date( latestTimeUTC );
	//document.writeln( 'Earliest Time: ' + new Date( earliestTimeUTC ) + ' <br />' );
	//document.writeln( '  Latest Time: ' + new Date( latestTimeUTC ) + ' <br />' );
	if ( pTimeUTCMs < earliestTimeUTC ) 
	{
		//document.writeln( '<h4>Debug: Computed Time is before earliest time.<br />Computed: ' + new Date( pTimeMs ) + ' <br />Earliest Time: ' + new Date( earliestTime ) + '</h4> ' );
		// SetStatus( 'RenderTime: ' + new Date( pTimeUTCMs ) + '<br /> is before earliest Time: ' + new Date( earliestTimeUTC ) );
		return null;
	}
	if ( pTimeUTCMs > latestTimeUTC ) 
	{
		// SetStatus( 'RenderTime: ' + new Date( pTimeUTCMs ) + '<br /> is after Latest Time: ' + new Date( latestTimeUTC ) );
		//-----
		// changed to return last known position
		// instead of null
		//	return null;
		pTimeUTCMs = latestTimeUTC;
		lastKnown = true;
	}
	// We now think the data is something we can draw 
	// -- not out of bounds --
	var thisTrack = pVehicle.tracks[0];
	var nextTrack;
	for ( var x = 1; x < TrackLength; x++ )
	{
		var nextTrack = pVehicle.tracks[x];
		var fromTime = thisTrack.timestamp;
		var toTime = nextTrack.timestamp;

		var debugFromTime = new Date( fromTime );
		var debugToTime = new Date( toTime );

		if ( fromTime <= pTimeUTCMs && pTimeUTCMs <= toTime )
		{
			//{ debugger; }
			var latDiff = (nextTrack.lat-thisTrack.lat);
			var computeTimeDiff = ( pTimeUTCMs - thisTrack.timestamp );
			var newLat = thisTrack.lat + (nextTrack.lat-thisTrack.lat)*( pTimeUTCMs - thisTrack.timestamp )/(nextTrack.timestamp - thisTrack.timestamp );
			var newLon = thisTrack.lon + (nextTrack.lon-thisTrack.lon)*( pTimeUTCMs - thisTrack.timestamp )/(nextTrack.timestamp - thisTrack.timestamp );
			var useHeading = Mod( thisTrack.heading, 360 );
			var useSpeed = thisTrack.speed;
			// Save this track for next loop calculation
			thisTrack = nextTrack;

			// Jump out if we got one
			return { lat: newLat, lon: newLon, heading: useHeading, speed: useSpeed, timestamp: pTimeUTCMs, lastknown: lastKnown };
		} else {
			thisTrack = nextTrack;
		}
	}
}


function Mod(X, Y) {
    return X - Math.floor(X / Y) * Y;
}

function mod(X, Y) {
    var t;
    t = X % Y;
    return t < 0 ? t + Y : t;
}

function offsetImage(iconAnchor) {
   var lZoom = oMap.GetZoomLevel();
   iconAnchor.ImageOffset = new VEPixel(-(lZoom * 2), -(lZoom * 3));
}

function updateImageOffset() {
   for (var i = 0; i < oMap.GetShapeLayerCount(); i++) {
      var shapeLayer = oMap.GetShapeLayerByIndex(i);
      if (shapeLayer != null) {
         for (var j = 0; j < shapeLayer.GetShapeCount(); j++) {
            var shape = shapeLayer.GetShapeByIndex(j);
            if (shape != null) {
               var iconAnchor = shape.GetCustomIcon();
               if (iconAnchor != null) offsetImage(iconAnchor);
            }
         }
      }
   }
}


