/*
 EnergyMap.js
 
 The OpenLayers and OpenStreetMap based viewer for embedded energy data
 */

var lon = 10.0;
var lat = 51.0;
var lastMapZoom = 5;

var epsg4326 = new OpenLayers.Projection("EPSG:4326");
var map;
var baseTileLayer;
// var markers;
var vectors;
var popup;

var allVisibleGeoRegions = new Array();
var visibleTileRegion = new Array();    /* 0 = l, 1 = t, 2 = r, 3 = b */
var currentlyProcessedTileIndices = new Array();

var allVisibleMarkers = new Dictionary();

var debuggingEnabled = false;
var tracingEnabled = false;

// var layer, gml;

var energySitesMarkerLayer = null;

var baseDebuggingMapLayerTilePath = "file:///Volumes/Kamcatka/Users/Tomi/Library/Caches/org.objectfarm.FarmersOpenMap/tile.openstreetmap.org/";

var baseEnergyMapResourcePath = "http://www.energymap.info/";
// var baseEnergyMapResourcePath = "file:///Volumes/Ocean/Domains/energymap.info/WebServer/Documents/";
// var baseEnergyMapResourcePath = "file:///Volumes/Kamcatka/Users/Tomi/Library/Application%20Support/FarmersOpenMap/EnergyMapSite/";
var localMapDataEnabled = false;

var baseEnergyMapTileImagePath = baseEnergyMapResourcePath + "tiles/e4l/";
var baseEnergyMapTileDataPath = baseEnergyMapResourcePath + "tiles/json.e4l/";
var baseEnergyMapEnergySitesDataPath = "http://www.energymap.info:8080/device?id="; // ?id=



/*
 DEBUGGING FUNCTIONALITY
 */


/*
 ENERGYMAP FUNCTIONALITY
 */


function init()
{
    if( tracingEnabled ) console.log( "EnergyMap " );
    if( tracingEnabled ){
		console.log("Cookies are: " );
		Cookie.dump();
    }

    // Prepare some UI elements
    //
    hideEnergySiteDetails();
    // hideVisibleGeoRegions();
    
    // Prepare the map
    //
    defineDefaultMapPosition();
    
    OpenLayers.ProxyHost = "proxy.cgi?url=";
    OpenLayers.Feature.Vector.style['default'].strokeWidth = 4;
    OpenLayers.Feature.Vector.style['default'].cursor = 'pointer';
    
/*
    { maxExtent: new OpenLayers.Bounds(-20037508.34,-20037508.34,20037508.34,20037508.34),
    numZoomLevels: 19,
    maxResolution: 156543.0399,
*/
        
    map = new OpenLayers.Map('map_view', 
                             { maxExtent: new OpenLayers.Bounds(-20037508,-20037508,20037508,20037508),
                             numZoomLevels: 19,
                             maxResolution: 156543,
                             units: 'm',
                             projection: new OpenLayers.Projection("EPSG:900913"),
                             displayProjection: new OpenLayers.Projection("EPSG:4326"),
                             controls: [ new OpenLayers.Control.PanZoomBar(),
                                        new OpenLayers.Control.LayerSwitcher(),
                                        new OpenLayers.Control.Navigation() ]
                             });
    
    var layerOpenStreetBrowser = new OpenLayers.Layer.OSM("OpenStreetBrowser", "/tiles/base/", {numZoomLevels: 19});
    var layerMapnik = new OpenLayers.Layer.OSM.Mapnik("Standard (Mapnik)");
    var layerTah = new OpenLayers.Layer.OSM.Osmarender("Standard (Osmarender)");
  	
    var layerDebuggingMap = new OpenLayers.Layer.OSM("Debugging Base Map",
                                                    baseDebuggingMapLayerTilePath,
                                                    { type: 'png',
                                                    displayOutsideMaxExtent: true, isBaseLayer: true,
                                                    transparent: false, "visibility": true });
    
    var layerHillshading = new OpenLayers.Layer.OSM("Hillshading (NASA SRTM3 v2)",
                                                    "http://toolserver.org/~cmarqu/hill/",
                                                    { type: 'png',
                                                    displayOutsideMaxExtent: true, isBaseLayer: false,
                                                    transparent: true, "visibility": true });
    
    var layerEnergyMap = new OpenLayers.Layer.OSM(
                                                  "EnergyMap",
                                                  baseEnergyMapTileImagePath,
                                                  { type: 'png',
                                                  displayOutsideMaxExtent: true,
                                                  isBaseLayer: false,
                                                  numZoomLevels: 12,
                                                  transparent: true,
                                                  "visibility": true });
    if( localMapDataEnabled )
    {
        baseTileLayer = layerDebuggingMap;
    }
    else
    {
        baseTileLayer = layerMapnik;
    }
    
    map.addLayers([ baseTileLayer, layerEnergyMap]);
    
    energySitesMarkerLayer = new OpenLayers.Layer.Markers("Markers", {
                                                          displayInLayerSwitcher: false,
                                                          numZoomLevels: 19,
                                                          maxExtent: new OpenLayers.Bounds(-20037508,-20037508,20037508,20037508),
                                                          maxResolution: 156543,
                                                          units: "m",
                                                          projection: "EPSG:900913"
                                                          });
    map.addLayer( energySitesMarkerLayer );
    
    /* map.addControl(new OpenLayers.Control.LayerSwitcher());
     map.addControl(new OpenLayers.Control.Permalink()); */
    
    
    
    /* Set the current start location */
    
	if( Cookie.get("_energymap_location") ) {
		var	cookieValues = Cookie.get("_energymap_location").split("|");
        
		lon = cookieValues[0];
		lat = cookieValues[1];
		lastMapZoom = cookieValues[2];
        
		var centerLocation = new OpenLayers.LonLat( lon, lat );
		setMapCenter( centerLocation, lastMapZoom );
	}
	else {
		var centerLocation = new OpenLayers.LonLat( lon, lat );
		setMapCenter( centerLocation, lastMapZoom );
	}
    
    if( tracingEnabled ) console.log("Center GPS: lon=" + lon + " lat=" + lat + " z="+lastMapZoom );
    
    
    /* Set the current start location */
    
    if (!map.getCenter()) { 
        /* gml = new OpenLayers.Layer.GML("Windenergie", "osm/sutton_coldfield.osm", {format: OpenLayers.Format.OSM});  */
        /* map.zoomToExtent(new OpenLayers.Bounds(-1.819072,52.549034,-1.814341,52.551582));  */
        
    } else {
        if (map.getZoom() >= 11) {
            /* gml = new OpenLayers.Layer.GML("OSM", "http://www.openstreetmap.org/api/0.5/map?bbox=" + map.getExtent().toBBOX(), {format: OpenLayers.Format.OSM}); */
        } else {
            /* gml = new OpenLayers.Layer.GML("OSM", "xml/cambridgeport.osm", {format: OpenLayers.Format.OSM}); */
        }    
    }    
	/* 
     gml.events.register("loadstart", null, function() { $("map_feature_info").innerHTML = "Loaaaading…"; })
     gml.events.register("loadend", null, function() { $("map_feature_info").innerHTML = ""; })
     map.addLayer(gml);
     
     gml.preFeatureInsert = style_osm_feature;  */
    
	/*
     var sf = new OpenLayers.Control.SelectFeature(gml, {'onSelect': on_feature_hover});
     map.addControl(sf);
     sf.activate();
     */
    
    
    /* Make sure to remember the last location */
    
    map.events.register("zoomend", map, mapDidUpdateZoom );
    map.events.register("moveend", map, mapDidUpdateLocation );
    mapDidUpdateLocation();
    
}

function requestDataForVisibleTileArea( )
{
	// Clear out our memory of the visible regions
    //
	allVisibleGeoRegions = new Array();
	currentlyProcessedTileIndices = new Array();
    
    // @NOTE ... this is not perfect ... because it causes some "HTML flicker" during interaction
    // We should perhaps track the pending requests or set a timer for this redraw
    //
    drawVisibleGeoRegions( allVisibleGeoRegions );
    
	var bounds = baseTileLayer.getExtent();
    if( debuggingEnabled ) console.log("Map l: " + bounds.left + " b:" + bounds.bottom + " r:" + bounds.right + " t:" + bounds.top );
    
	var upperLeftTileIndex = tileIndexForLeftTileInPxBound( [bounds.left, bounds.top] );
	var lowerRightTileIndex = tileIndexForLeftTileInPxBound( [bounds.right, bounds.bottom] );
    
	visibleTileRegion[0] = upperLeftTileIndex[0];
	visibleTileRegion[1] = upperLeftTileIndex[1];
	visibleTileRegion[2] = lowerRightTileIndex[0];
	visibleTileRegion[3] = lowerRightTileIndex[1];  
	visibleTileRegion[4] = upperLeftTileIndex[2];   /* z */
    
	for( var x = visibleTileRegion[0]; x <= visibleTileRegion[2]; ++x )
	{
        for( var y = visibleTileRegion[1]; y <= visibleTileRegion[3]; ++y )
        {
            var jsonTileDataPath = baseEnergyMapTileDataPath + visibleTileRegion[4] + "/" + x + "/" + y + ".json";
     		if( tracingEnabled ) console.log("Getting data for: " + jsonTileDataPath );
            
            jQuery.ajax({
                        url: jsonTileDataPath,
                        type: 'GET',
                        dataType: 'json',
                        timeout: 10000,
                        success: processJSONDataForMapTile
                        });
            
        }
	}
}

function processJSONDataForMapTile( jsonData )
{
    if( tracingEnabled ) console.log("JSON result: " + jsonData );
    
    if( jsonData == null ) return;
    
    var theTileIndex = jsonData.tileIndex;
    
    // are we really a result set for this region ?
    //
    if( theTileIndex[2] != visibleTileRegion[4] ||
       theTileIndex[0] < visibleTileRegion[0] ||
       theTileIndex[0] > visibleTileRegion[2] ||
       theTileIndex[1] < visibleTileRegion[1] ||
       theTileIndex[1] > visibleTileRegion[3] ) return;
    
    // Is this really the first time we receive this dataset ?
    //
    
    var uniqueTileID = theTileIndex[0] + "_" + theTileIndex[1] + "_" + theTileIndex[2];
    
    if( currentlyProcessedTileIndices.indexOf( uniqueTileID ) > - 1 )
        return;
    
    currentlyProcessedTileIndices.push( uniqueTileID );
    
    if( tracingEnabled ) console.log("Adding JSON result for tile: x:" + theTileIndex[0] + " y:" + theTileIndex[1] + " z:" + theTileIndex[2] );
    
    // Insert the included georegions into our set and resort and update is list.
    //
    if( jsonData.geoRegions != null &&
        jsonData.geoRegions.length > 0 )
    {
        var newRegions = jsonData.geoRegions;
        
        allVisibleGeoRegions = allVisibleGeoRegions.concat( newRegions );
        
        allVisibleGeoRegions.sort( function(a,b) { 
                                  if( a.name == b.name ) return 0;
                                  if( a.name < b.name ) return -1;
                                  return 1;
                                  } );
        
        drawVisibleGeoRegions( allVisibleGeoRegions );
    }

    // If there are any energySites included ... prepare the markers.
    //
    
    if( jsonData.energySites != null )
    {
        var newEnergySites = jsonData.energySites;
        
        for (var i = 0; i < newEnergySites.length; ++i)
        {
            addMarkerForEnergySite( newEnergySites[i] );
        }        
    }
}

function addMarkerForEnergySite( siteReference )
{

    if( debuggingEnabled ) console.log("Energy site: " + siteReference );
    
    // See if this is really new ...
    //
    var uniqueMarkerID = null;
    
    if( siteReference.e4lID != null )
        uniqueMarkerID = siteReference.e4lID;
        
    else if( siteReference.detailsLink != null )
        uniqueMarkerID = siteReference.detailsLink;

    // Place a marker to the details data ... if there really is any data to show
    //
    if( uniqueMarkerID != null )
    {
        // Do we know that object already ?
        //
        if( allVisibleMarkers.hasObjectForKey( uniqueMarkerID ) == false )
        {
            var siteMarkerIcon = newMarkerForTypeID( siteReference.siteClass );
            var siteGPSLonLat = new OpenLayers.LonLat( siteReference.lon, siteReference.lat ).transform( epsg4326, map.getProjectionObject() );
            
            var siteMarker = new OpenLayers.Marker(siteGPSLonLat,siteMarkerIcon);
            siteMarker.events.register('mousedown', siteReference, siteMarkerMouseDown );

            energySitesMarkerLayer.addMarker( siteMarker );
            
            // The "detailsLink" is a unique key ... we will use it as such
            //
            allVisibleMarkers.setObjectForKey( siteMarker, uniqueMarkerID );
        }
        else
        {
            if( debuggingEnabled ) console.log("SKIPPING known Marker: " + uniqueMarkerID );
        }
    }    
}

function removeAllEnergySiteMarkers( )
{
    var allMarkerIDs = allVisibleMarkers.allKeys();
    
    for (var i = 0; i < allMarkerIDs.length; ++i)
    {
        removeEnergySiteMarkersWithID( allMarkerIDs[i] );
    }        
}

function removeEnergySiteMarkersWithID( markerID )
{
    var theMarker = allVisibleMarkers.objectForKey( markerID );
    
    if( theMarker != null )
    {
        allVisibleMarkers.removeObjectForKey( markerID );
        energySitesMarkerLayer.removeMarker( theMarker );
        theMarker.destroy();
    }
}

function siteMarkerMouseDown( evt )
{
    if( debuggingEnabled ) console.log("DOWN Marker: " + evt + " this " + this );

    fetchEnergySiteDetails( this );
    
    OpenLayers.Event.stop(evt);
}

function newMarkerForTypeID( markerTypeID )
{
    
    var size = new OpenLayers.Size(20,20);
    var offset = new OpenLayers.Pixel( -(size.w/2), -size.h );
    
    var iconPath = baseEnergyMapResourcePath + "img/logo-solarzeitalter_40.png";
    
    if( markerTypeID == "sun" )
    {
        iconPath = baseEnergyMapResourcePath + "img/logo-solarstrom_40.png";
    }
    else if( markerTypeID == "biomass" )
    {
        iconPath = baseEnergyMapResourcePath + "img/logo-biomasse_40.png";
    }
    else if( markerTypeID == "wind" )
    {
        iconPath = baseEnergyMapResourcePath + "img/logo-windkraft_40.png";
    }
    else if( markerTypeID == "water" )
    {
        iconPath = baseEnergyMapResourcePath + "img/logo-wasserkraft_40.png";
    }
    else if( markerTypeID == "earth" )
    {
        iconPath = baseEnergyMapResourcePath + "img/logo-geothermie_40.png";
    }
    else if( markerTypeID == "gas" )
    {
        iconPath = baseEnergyMapResourcePath + "img/logo-gase_40.png";
    }
    var newIcon = new OpenLayers.Icon( iconPath, size, offset );
    
    return newIcon;
}

function fetchEnergySiteDetails( energySiteReference )
{
    if( energySiteReference.e4lID != null )
    {
        var jsonEnergySiteDataPath = baseEnergyMapEnergySitesDataPath + energySiteReference.e4lID;
        if( debuggingEnabled ) console.log("Getting data for: " + jsonEnergySiteDataPath );
        
        jQuery.ajax({
                    url: jsonEnergySiteDataPath,
                    type: 'GET',
                    dataType: 'json',
                    timeout: 10000,
                    success: processJSONDataForEnergySite
                    });
    }    
    
}

function processJSONDataForEnergySite( jsonData )
{
    if( debuggingEnabled ) console.log("JSON Site result: " + jsonData.iconLink );
    
    if( jsonData == null ) return;
    
    // Currently the last dataset that we receive will be placed inside the HTML page
    //
    drawEnergySiteDetails( jsonData );
}    

function showVisibleGeoRegions( )
{
    document.getElementById('map_region_info_box').style.display = 'block';
}

function hideVisibleGeoRegions( )
{
    document.getElementById('map_region_info_box').style.display = 'none';
    clearVisibleGeoRegions();
}

function clearVisibleGeoRegions( )
{
    var	regionInfoString = "";
    if( document.getElementById('map_region_info') )
        document.getElementById('map_region_info').innerHTML = regionInfoString;
}

function showEnergySiteDetails( )
{
    document.getElementById('map_feature_info_box').style.display = 'block';
}

function hideEnergySiteDetails( )
{
    document.getElementById('map_feature_info_box').style.display = 'none';
    clearEnergySiteDetails();
}

function clearEnergySiteDetails( )
{
    var	siteInfoString = "";
    if( document.getElementById('map_feature_info') )
        document.getElementById('map_feature_info').innerHTML = siteInfoString;
}

function tileIndexForLeftTileInPxBound( aPxArray )
{    
	var xLonPx = aPxArray[0];
	var yLatPx = aPxArray[1];
    
    var res = baseTileLayer.getResolution();
    var x = Math.round((xLonPx - baseTileLayer.maxExtent.left) / (res * baseTileLayer.tileSize.w));
    var y = Math.round((baseTileLayer.maxExtent.top - yLatPx) / (res * baseTileLayer.tileSize.h));
    var z = baseTileLayer.map.getZoom();
    var limit = Math.pow(2, z);
    
    if (y < 0 || y >= limit)
    {
	    /* An invalid tile */
        return [-1, -1, -1];
    }
    else
    {
        x = ((x % limit) + limit) % limit;
    }
    
    if( debuggingEnabled ) console.log("Map x: " + x + " y:" + y + " z:" + z );
    
	return [x, y, z];
}

function mapDidUpdateZoom()
{
    // A "zoom" always clears the site details because it might no longer be selected or visible
    //
    hideEnergySiteDetails();
    
	var aZoom = map.getZoom();
    
    // If we zoomed out we will remove all markers ... they might be incorrect.
    //
    if( lastMapZoom > aZoom )
    {
        removeAllEnergySiteMarkers();
    }
    lastMapZoom = aZoom;
    
    mapDidUpdateLocation();
}

function mapDidUpdateLocation()
{
	/* We will push the information about the new center and zoom into all relevant places */
    
	var aLonLat = map.getCenter();
	var aGPSLonLat = aLonLat.clone().transform(map.getProjectionObject(), epsg4326);
    
	var aZoom = map.getZoom();
    
	var aMapLocation = aGPSLonLat.lon + "|" + aGPSLonLat.lat + "|" + aZoom;
    
	Cookie.set("_energymap_location", aMapLocation, 80000 );
    if( debuggingEnabled ) console.log("GPS Cookie: " + aMapLocation );
    
	requestDataForVisibleTileArea();
    
    var lonString = aGPSLonLat.lon;
    var latString = aGPSLonLat.lat;   // @TODO ... max 5 digits!
    
	document.getElementById('map_center_gps_info').innerHTML = "Lon: "+ lonString + ", Lat: " + latString;
}

function setMapCenter(center, zoom)
{
    zoom = parseInt(zoom);
    var numzoom = map.getNumZoomLevels();
    if (zoom >= numzoom) zoom = numzoom - 1;
    map.setCenter(center.clone().transform(epsg4326, map.getProjectionObject()), zoom);
}

function setMapExtent(extent)
{
    map.zoomToExtent(extent.clone().transform(epsg4326, map.getProjectionObject()));
}




