function dotMarker(lat, lng) {
    var result;
    var dotIcon = new GIcon();
    dotIcon.image="http://blogs.sun.com/vipul/resource/images/Blue_dot.png";
    dotIcon.iconSize=new GSize(18,18); //32, 18
    dotIcon.iconAnchor=new GPoint(9,9); //16, 9

    result = new GMarker(new GLatLng(lat, lng),{
        icon:dotIcon
    });
    return result;
}

function dateToTimestamp(d) {
    var ts = "";

    ts = d.getFullYear() + "/" +
    ((d.getMonth() < 9) ? "0" : "") + (d.getMonth() + 1) + "/" +
    ((d.getDate() < 10) ? "0" : "") + d.getDate() + " " +
    ((d.getHours() < 10) ? "0" : "") + d.getHours() + ":" +
    ((d.getMinutes() < 10) ? "0" : "") + d.getMinutes() + ":" +
    ((d.getSeconds() < 10) ? "0" : "") + d.getSeconds() + "." +
    d.getMilliseconds();

    return ts;
}

function getTimestamp(millis) {
    var d = new Date();

    d.setTime(millis);
    return dateToTimestamp(d);
}

function animateMap(val, dataSet, options) {
    //alert("animateMap called for val=" + val + "replayTime=" + options.replayTime);
    updateMap(val, dataSet, options, true);
    if (val + 1 <= 100) {
        setTimeout(function () {
            animateMap(val + 1,dataSet,options);
        },
        options.replayTime*10);
    }
}

function updateMap(val, dataSet, options, alwaysUpdate) {
    var now = (new Date()).getTime();
    if (!alwaysUpdate && (now < options.lastSliderUpdateTime + 100)) return;

    options.lastSliderUpdateTime = now;
    var totalTime = dataSet[dataSet.length - 1].timestampInMillis
    - dataSet[0].timestampInMillis;
    var elapsedTime = (totalTime * val)/100 + dataSet[0].timestampInMillis;
    document.getElementById("time" + options.vizid).innerHTML =
        getTimestamp(elapsedTime);

    // Find the datapoint with a timestamp closest to elapsedTime
    var low = 0;
    var high = dataSet.length;

    // The search range is always dataSet[low] through dataSet[high - 1]
    while (low < (high - 1)) {
        idx = low + Math.floor((high - low)/2);
        if (elapsedTime < dataSet[idx].timestampInMillis) {
            high = idx;
        } else {
            low = idx;
        }
    }


    if (options.interpolate) {
        if (high == dataSet.length) {
            options.marker.setPoint(new GLatLng(dataSet[low].values[options.latIdx],
                dataSet[low].values[options.lngIdx]));
            document.getElementById("distance" + options.vizid).innerHTML =
                (options.distances[low]/1609.344).toFixed(2);
        } else {
            var lat1 = dataSet[low].values[options.latIdx];
            var lat2 = dataSet[high].values[options.latIdx];
            var lng1 = dataSet[low].values[options.lngIdx];
            var lng2 = dataSet[high].values[options.lngIdx];
            var t1 = dataSet[low].timestampInMillis;
            var t2 = dataSet[high].timestampInMillis;
            var m = (elapsedTime - t1)/(t2 - t1);
            options.marker.setPoint(new GLatLng(lat1 + (lat2 - lat1)*m,
                lng1 + (lng2 - lng1)*m));
            document.getElementById("distance" + options.vizid).innerHTML =
            ((options.distances[low] + m*(options.distances[high] - options.distances[low]))/1609.344).toFixed(2);
        }
    } else {
        options.marker.setPoint(new GLatLng(dataSet[low].values[options.latIdx],
            dataSet[low].values[options.lngIdx]));
        document.getElementById("distance" + options.vizid).innerHTML =
            (options.distances[low]/1609.344).toFixed(2);
    }

}

// dataSet: array of lat/lng and timestamp info (passed in)
// options: passed in, e.g. width, height etc
// marker: passed in and gets initialized
// distances: an empty array variable that is passed in and gets initialized
function drawMap(dataSet, options) {
    // Write out the divs for the map and slider
    //alert(options.vizid);
    if (options.vizid != "dynamicmaptrace") {
    document.write("<div id='" + options.vizid + "' style='width: " +
        options.width + "px; height: " + options.height + "px'></div>");
    document.write("<div class='slider' id='slider" + options.vizid + "' style='width: " +
        (options.width - 8) + "px;'></div>");
    document.write("<span style='" + options.fontStyle + "'>Time: <span id='time" + options.vizid + "'>" +
        getTimestamp(dataSet[0].timestampInMillis) + "</span><br/>");
    document.write("Distance: <span id='distance" + options.vizid + "'>0.00</span></span>");
    }
    $("#slider" + options.vizid).slider({
        'stop': function (e, ui) {
            //var start = (new Date()).getTime();
            updateMap(ui.value, dataSet, options, true);
        //var end = (new Date()).getTime();
        //document.getElementById("slider-value").innerHTML = ui.value + ": " +
        //    "Update took (" + end + " - " + start + ") = " + (end - start) + "ms";
        },
        'slide': function (e, ui) {
//            var now = (new Date()).getTime();
//            if (now > options.lastSliderUpdateTime + 100) {
//                options.lastSliderUpdateTime = now;
                updateMap(ui.value, dataSet, options, false);
//            }
        }
    });

    // Create the map and the dot icon
    var map = new GMap2(document.getElementById(options.vizid));
    map.addControl(new GMapTypeControl());
    map.addControl(new GSmallZoomControl3D());
    map.setCenter(new GLatLng(options.centerLat,options.centerLng), 
        options.zoomLevel);

    // Mark the start and end points
    var startPoint = new GLatLng(dataSet[0].values[options.latIdx],
            dataSet[0].values[options.lngIdx]);
    var endPoint = new GLatLng(dataSet[dataSet.length - 1].values[options.latIdx],
            dataSet[dataSet.length - 1].values[options.lngIdx]);
    var startMarker = new GMarker(startPoint,G_START_ICON);
    map.addOverlay(startMarker);

    options.marker = dotMarker(dataSet[0].values[options.latIdx], dataSet[0].values[options.lngIdx]);
    map.addOverlay(options.marker);
    map.addOverlay(new GMarker(endPoint,G_END_ICON));

    // If interpolation is turned on, overlay the polygon
    options.distances = new Array(dataSet.length);
    var bounds = new GLatLngBounds();
    if (true || options.interpolate) {
        var polyPoints = new Array(dataSet.length);
        options.distances[0] = 0;
        polyPoints[0] = new GLatLng(dataSet[0].values[options.latIdx],
            dataSet[0].values[options.lngIdx]);
        bounds.extend(polyPoints[0]);
        for (var i = 1; i < dataSet.length; i++) {
            polyPoints[i] = new GLatLng(dataSet[i].values[options.latIdx],
                dataSet[i].values[options.lngIdx]);
            bounds.extend(polyPoints[i]);
            options.distances[i] = options.distances[i - 1] + polyPoints[i].distanceFrom(polyPoints[i-1]);
        }

        map.addOverlay(new GPolyline(polyPoints, options.polyLineColor,
            options.polyLineWidth));
        map.setZoom(map.getBoundsZoomLevel(bounds));
    }

    GEvent.addListener(startMarker, "click", function() {
        animateMap(0, dataSet, options);
    });
}

function drawApproximateLocation(options) {
    if (options.vizid != "dynamicmaptrace") {
        document.write("<div id='" + options.vizid + "' style='width: " +
            options.width + "px; height: " + options.height + "px'></div>");
    }

    // Create the map and the dot icon
    var map = new GMap2(document.getElementById(options.vizid));
    map.addControl(new GMapTypeControl());
    map.addControl(new GSmallZoomControl3D());
    map.setCenter(new GLatLng(options.centerLat,options.centerLng), 0);

    doDrawCircle(map, options.centerLat, options.centerLng, options.radius);
}

function doDrawCircle(map, lat, lng, circleRadius){
        var circle;
	var bounds = new GLatLngBounds();
	var circlePoints = Array();
        var circleUnits = 'KM';

	with (Math) {
		if (circleUnits == 'KM') {
			var d = circleRadius/6378.8;	// radians
		} else { //miles
			var d = circleRadius/3963.189;	// radians
		}

		var lat1 = (PI/180)* lat; // radians
		var lng1 = (PI/180)* lng; // radians

		for (var a = 0 ; a < 361 ; a++ ) {
			var tc = (PI/180)*a;
			var y = asin(sin(lat1)*cos(d)+cos(lat1)*sin(d)*cos(tc));
			var dlng = atan2(sin(tc)*sin(d)*cos(lat1),cos(d)-sin(lat1)*sin(y));
			var x = ((lng1-dlng+PI) % (2*PI)) - PI ; // MOD function
			var point = new GLatLng(parseFloat(y*(180/PI)),parseFloat(x*(180/PI)));
			circlePoints.push(point);
			bounds.extend(point);
		}

		if (d < 1.5678565720686044) {
			circle = new GPolygon(circlePoints, '#4e92d1', 1, 0.85, '#4e92d1', 0.25);
		} else {
			circle = new GPolygon(circlePoints, '#4e92d1', 1, 1);
		}

		map.addOverlay(circle);
		map.setZoom(map.getBoundsZoomLevel(bounds));
	}
}