/**
 * Uses an anonymous function to create the Cyclestreets Namespace.
 */

(function(){

  // Create the Cyclestreets Namespace
  var CycleStreets = {
    
  // Bound to the OpenLayers map
  map: null,
  
  // Offline mode - which only uses local resources such as locally generated tiles.
  offlineMode: false,

  // http://www.cyclestreets.ibsen/tiles/
  localTileHost: null,

  // Tiles and route planning based on this cyclestreets data layer. Risk of confusion with OpenLayers layers.
  layer: null,

  // Whether the default map should be an uncluttered map style (e.g. on the itinerary page)
  defaultUnclutteredStyle: false,

  // Whether to include extra layers for help debugging routing, eg fine line style
  extraMapLayers: false,

  // Use google layers such as streets and satellite, only really necessary with Photomap
  googleLayers: true,

  // The document element in the requested xml file.
  doc: null,

  // The tag that is usually underneath the map is the place to put passive map messages.
  messageElement: null,
  
  // A popup, placed in this class so that the itinerary class can access it #!# hacky!
  popup: null,

  baseUrl: null,
  themeLocation: null,
  debugtag: null,

  // In debug mode a debug tag is created that can take these messages.
  debugtagmsg: function (msg) {if (this.debugtag) {this.debugtag.innerHTML=msg + '<br />' + this.debugtag.innerHTML;}},

  map_message: function (text) {this.messageElement.innerHTML=text;},
  marker_tag: function (color, size) {return '<img class="marker_tag" src="' + CS.baseUrl + '/' + CS.themeLocation + 'mm_' + size + '_' + color + '.png" alt="' + color + ' marker" />';},
  icon_src: function (icon) {return CS.baseUrl + '/images/icons/' + icon + '.png';},

  // Very similar to maps.php's maps::icon()
  icon_tag: function (icon, altText, title) {
      altText = altText ? altText : 'Icon: ' + icon;
      title = title ? title : altText;
      return '<img src="' + this.icon_src(icon) + '" border="0" alt="' + altText + '" title="' + title + '">';
    },

  initialize: function (baseUrl, themeLocation, offlineMode, localTileHost, defaultUnclutteredStyle, extraMapLayers, googleLayers, baseLayerName) {

      this.baseUrl               = baseUrl;
      this.themeLocation         = themeLocation;
      this.offlineMode           = offlineMode;
      this.localTileHost         = localTileHost;
      this.defaultUnclutteredStyle = defaultUnclutteredStyle;
      this.extraMapLayers        = extraMapLayers;
      this.googleLayers		 = googleLayers;
      this.baseLayerName         = baseLayerName;

      this.debugtag = document.getElementById('debug');
    },

  createMap: function (divId, centre, zoom, zoomBar, restrictExtent) {

      // Default to unrestricted extent
      if(arguments.length < 5) {restrictExtent = false;}

      // Limit the initial zoom - to avoid whole world being shown. (This is temp fix).
      if(zoom > 17) {zoom = 17;}

      // Image to display when tile is not available
   OpenLayers.Util.onImageLoadError = function() {
      this.src = CS.baseUrl + '/images/general/cyclestreets_more_soon.png';
   };

      var restrictedExtent = null;
      if(restrictExtent) {
	
	restrictedExtent = new OpenLayers.Bounds();
	
	// British Isles: W of Tralee, S of Land's End, to NE of Shetland
	restrictedExtent.extend(this.lonLatToMercator(new OpenLayers.LonLat(-11, 49)));
	restrictedExtent.extend(this.lonLatToMercator(new OpenLayers.LonLat(2, 61)));
      }
      
   // The map and layers were initially based on http://www.opencyclemap.org/
   // http://trac.openlayers.org/wiki/SphericalMercator
   // http://trac.openlayers.org/wiki/SettingZoomLevels
   this.map = new OpenLayers.Map(divId,
    { maxExtent: new OpenLayers.Bounds(-20037508.34,-20037508.34,20037508.34,20037508.34),
	restrictedExtent: restrictedExtent,
	numZoomLevels: 19,
	maxResolution: 156543.0339,
	units: 'm',
	controls: [new OpenLayers.Control.LayerSwitcher(),
		   // Permalink is only really useful for experienced users.
		   new OpenLayers.Control.Permalink('permalink'),
		   new OpenLayers.Control.ScaleLine(),
		   // Should be enabled for developers probably.
		   // new OpenLayers.Control.Permalink('editlink', 'http://www.openstreetmap.org/edit.html'),
		   new OpenLayers.Control.Attribution(),//{div: document.getElementById("mapAttribution")}),
		   // PZ is a local subclass that provides only zoom in and out (and pan), but not 'zoom to whole world'.
		   (zoomBar ? new OpenLayers.Control.PanZoomBar() : new OpenLayers.Control.PZ()),
		   new OpenLayers.Control.Navigation(),
		   // Note: it seems this must remain commented in to ensure IE6 compatibility.
		   // Quite why this is the case has yet to be established, as it is hard to debug JavaScript in IE.
		   new OpenLayers.Control.MousePosition()
		   ],
	projection: new OpenLayers.Projection("EPSG:900913"),
	displayProjection: new OpenLayers.Projection("EPSG:4326"),
	
	eventListeners: {"changebaselayer": this.mapBaseLayerChanged}
    });


      // Add the Editlink control, which can be useful. It needs to be added in the same circumstances that create the editlink element: see slippyMap::mapContainerSection().
      if(this.extraMapLayers) {
	this.map.addControl(new OpenLayers.Control.Permalink('osmViewLink', 'http://www.openstreetmap.org/'));
	// Suggested by Shaun 14 April 2009 - Implemented Simon 7 July 2009
	this.map.addControl(new OpenLayers.Control.Permalink('keepRightLink', 'http://keepright.ipax.at/report_map.php?ch30=0&ch40=1&ch50=1&ch60=1&ch70=1&ch90=0&ch100=0&ch110=0&ch120=1&ch130=1&ch150=1&ch160=1&ch170=1&ch180=1&ch190=1&ch200=1&ch210=1&ch220=1&ch191=1&ch192=1&ch193=1&ch194=1&ch201=1&ch202=1&ch203=1&ch204=1&show_ign=1&show_tmpign=1'));
	// Another suggestion from Shaun
	this.map.addControl(new OpenLayers.Control.Permalink('openStreetBugsLink', 'http://openstreetbugs.schokokeks.org/?layers=0B0T'));
	// Martin found this one:
	this.map.addControl(new OpenLayers.Control.Permalink('dupeNodes', 'http://matt.dev.openstreetmap.org/dupe_nodes/?layers=BT'));
      }

	  
	// Register the uncluttered style if required
      // Use buffer:0 to minimize the surrounding tiles loaded.
	if(this.defaultUnclutteredStyle) {
		var unclutteredStyle = new OpenLayers.Layer.OSM("Simple style",
			["http://a.tile.cloudmade.com/8bafab36916b5ce6b4395ede3cb9ddea/3697/256/${z}/${x}/${y}.png",
			 "http://b.tile.cloudmade.com/8bafab36916b5ce6b4395ede3cb9ddea/3697/256/${z}/${x}/${y}.png",
			 "http://c.tile.cloudmade.com/8bafab36916b5ce6b4395ede3cb9ddea/3697/256/${z}/${x}/${y}.png"],
			{attribution: '(c) OpenStreetMap and contributors, CC-BY-SA; Map images (c) <a href="http://www.cloudmade.com">CloudMade</a>', buffer:0});
	}
	
	
   // Consider adding more layers as per the examples at
   // http://trac.openlayers.org/browser/trunk/openlayers/examples/spherical-mercator.html
   var cycle = new OpenLayers.Layer.OSM("OpenCycleMap (shows hills)",
					["http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png",
					 "http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png",
					 "http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"],
    {attribution: '(c) OpenStreetMap and contributors, CC-BY-SA; <a class="mapkey" href="http://www.gravitystorm.co.uk/shine/cycle-info/">OpenCycleMap.org - <strong>Map key</strong></a>', numZoomLevels: 18, buffer:0});

   // Cloudmade styles are controlled by a style number - the first number after the API key and before 256.
   // To view style numbers go to http://maps.cloudmade.com/ and choose a style you like and then click share and 
   // look for styleId= in the url.
     if(this.extraMapLayers) {
       var fineline = new OpenLayers.Layer.OSM("Fine Line style",
                                         ["http://a.tile.cloudmade.com/8bafab36916b5ce6b4395ede3cb9ddea/2/256/${z}/${x}/${y}.png",
                                          "http://b.tile.cloudmade.com/8bafab36916b5ce6b4395ede3cb9ddea/2/256/${z}/${x}/${y}.png",
					  "http://c.tile.cloudmade.com/8bafab36916b5ce6b4395ede3cb9ddea/2/256/${z}/${x}/${y}.png"],
                                         {attribution: 'Map images copyright <a href="http://www.cloudmade.com">CloudMade</a>', buffer:0});
     }

      if(this.extraMapLayers || this.offlineMode) {
	// The buffer parameter controls the number of tiles beyond the viewport that are also downloaded.
	var cyclestreets = new OpenLayers.Layer.OSM("CycleStreets", this.localTileHost + '${z}/${x}/${y}.png',
    {attribution: 'Provided by <a href=\"http://www.cyclestreets.net/\">CycleStreets</a>', numZoomLevels: 22, buffer:0});
      }
   if(this.offlineMode) {
     this.map.addLayer(cyclestreets);
   } else {
     if(this.defaultUnclutteredStyle) {
	 	this.map.addLayer(unclutteredStyle);
	 }
	 this.map.addLayer(cycle);
     this.googleEtcLayers();

     if(this.extraMapLayers) {
       this.map.addLayer(fineline);
       this.map.addLayer(cyclestreets);
     }
   }

   if(this.baseLayerName) {this.selectNamedBaseLayer(this.baseLayerName);}

   if (!this.map.getCenter() && centre) {this.map.setCenter(centre, zoom);}
   return this.map;
  },

  // A null function that is redefined by some pages.
  mapBaseLayerChanged: function (event) {},

  selectNamedBaseLayer: function (name) {
      // alert('Setting layer named: ' + name);
      if(!name) {return;}
      var len = this.map.layers.length;
      for (var i=0; i < len; i++) {
	var layer = this.map.layers[i];
	if(!layer.isBaseLayer) {continue;}
	if(name == layer.name) {
	  CS.map.setBaseLayer(layer);
	  break;
	}
      }
    },

//selectNamedBaseLayer('{$location_row['basemap']}');
	
	
	// Function to create the map instance, if it does not already exist
// #!# This was originally in photomap.js and should be refactored out
	createMap2: function(longitude, latitude, zoom)
	{
		// Set the map to the centre point, or create the map if it does not exist
		var centre = CS.lonLatToMercator(new OpenLayers.LonLat(longitude, latitude));
		if(CS.map) {
			CS.map.setCenter(centre, zoom);
		} else {
			CS.map = CS.createMap('map', centre, zoom, true);
		}
	},
	
	
	


  // Helper function to ensure zoomed into suitable position
  focusLonLat: function (saytResult) {

      // Can't use 'this' because its called from outside, so use CS instead.

      var ll = CS.lonLatToMercator(new OpenLayers.LonLat(saytResult.longitude, saytResult.latitude));

      if(CS.map.getZoom() < 16) {
		CS.map.moveTo(ll, 16);
      } else {
		CS.map.panTo(ll);
      }
    },

  // Layers provided by google and others
  googleEtcLayers: function () {

      if(!this.googleLayers) {return;}

      // Make sure these are commented out when other layers are not loaded. Else get "G_SATELLITE_MAP undefined" errors.

      // create Google Mercator layers
      var gmap = new OpenLayers.Layer.Google('Google Streets', {'sphericalMercator': true});
      var gsat = new OpenLayers.Layer.Google('Google Satellite', {type: G_SATELLITE_MAP, 'sphericalMercator': true});

    /* #3rdPartyLayers - use this hashtag to find related references
            var ghyb = new OpenLayers.Layer.Google(
                'Google Hybrid',
                {type: G_HYBRID_MAP, 'sphericalMercator': true}
            );
            // create Virtual Earth layers
            var veroad = new OpenLayers.Layer.VirtualEarth(
                'Virtual Earth Roads',
                {'type': VEMapStyle.Road, 'sphericalMercator': true}
            );
            var veaer = new OpenLayers.Layer.VirtualEarth(
                'Virtual Earth Aerial',
                {'type': VEMapStyle.Aerial, 'sphericalMercator': true}
            );
            var vehyb = new OpenLayers.Layer.VirtualEarth(
                'Virtual Earth Hybrid',
                {'type': VEMapStyle.Hybrid, 'sphericalMercator': true}
            );

            // create Yahoo layer
            var yahoo = new OpenLayers.Layer.Yahoo(
                'Yahoo Street',
                {'sphericalMercator': true}
            );
            var yahoosat = new OpenLayers.Layer.Yahoo(
                'Yahoo Satellite',
                {'type': YAHOO_MAP_SAT, 'sphericalMercator': true}
            );
            var yahoohyb = new OpenLayers.Layer.Yahoo(
                'Yahoo Hybrid',
                {'type': YAHOO_MAP_HYB, 'sphericalMercator': true}
            );
	    
            CS.map.addLayers([gmap, gsat, ghyb, veroad, veaer, vehyb,
                           yahoo, yahoosat, yahoohyb]);
	    */
	    
            CS.map.addLayers([gmap, gsat]);
    },

    mercatorToLonLat: function (merc) {
    var lon = (merc.lon / 20037508.34) * 180;
    var lat = (merc.lat / 20037508.34) * 180;
    
    lat = 180/Math.PI * (2 * Math.atan(Math.exp(lat * Math.PI / 180)) - Math.PI / 2);
    
    return new OpenLayers.LonLat(lon, lat);
  },
    
    lonLatToMercator: function (ll) {
    var lon = ll.lon * 20037508.34 / 180;
    var lat = Math.log(Math.tan((90 + ll.lat) * Math.PI / 360)) / (Math.PI / 180);
    
    lat = lat * 20037508.34 / 180;
    
    return new OpenLayers.LonLat(lon, lat);
  },
    
    scaleToZoom: function (scale) {
    return Math.log(360.0/(scale * 512.0)) / Math.log(2.0);
    },
 

  /**
   * Helper functions
   * Rounds numbers to 6 / 7 figures to make them more readable.
   */
  r6: function (x) { return Math.round( 1000000 * x)/ 1000000; }, 
  r7: function (x) { return Math.round(10000000 * x)/10000000; },
  
	// Entity-safe string encoding
	htmlspecialchars: function(string) {
		// NB Uses jQuery
		return $('<span>').text(string).html();
	},
	
	// Convert newlines to HTML linebreaks; from http://stackoverflow.com/questions/2919337
	nl2br: function(str, is_xhtml) {
		var breakTag = (is_xhtml || typeof is_xhtml === 'undefined') ? '<br />' : '<br>';    
		return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1'+ breakTag +'$2');
	}
	
	
  };
 

  // Bind Cyclestreets in this particular place - to avoid 'polluting' the global namespace.
  // (This is the same trick used by OpenLayers.)
  window["http://www.cyclestreets.net/schema/xml/"] = CycleStreets;
 })();

// Setup CS object
var CS = window["http://www.cyclestreets.net/schema/xml/"];

//// Ends
