/*** Javascript General ***
  http://www.hunlock.com/blogs/Ten_Javascript_Tools_Everyone_Should_Have
***/
String.prototype.htmlEntities = function () {
  return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\"/g,'&quot;');
};
String.prototype.stripTags = function () {
  return this.replace(/<([^>]+)>/g,'');
};

/* Fix required for IE in OS information boxes when '<a href="">'
   tags don't work except with a right-click "Open". */
function goToTag(tag) {
  window.location = "#" + tag;
}

// Fix required for LightBox to work with Google Maps API
function pop(arg){
  myLightbox.start(arg);
}

/*** Cookies ***
  Useful reference: http://www.elated.com/articles/javascript-and-cookies/
***/
function set_cookie (name, value, path, domain, secure) {
  var cookie_string = name + "=" + escape(value);
  var expires = new Date(); // today
  expires.setFullYear(expires.getFullYear() + 1); // Expires in one year's time
  cookie_string += "; expires=" + expires.toGMTString();
  if (path) {
    cookie_string += "; path=" + escape(path);
  }
  if (domain) {
    cookie_string += "; domain=" + escape(domain);
  }
  if (secure) {
    cookie_string += "; secure";
  }
  document.cookie = cookie_string;
}

function delete_cookie (cookie_name) {
  var cookie_date = new Date (); // today
  cookie_date.setTime (cookie_date.getTime() - 1); // less than current time to clear the cookie
  document.cookie = cookie_name += "=; expires=" + cookie_date.toGMTString();
}

function get_cookie (cookie_name) {
  var results = document.cookie.match ('(^|;) ?' + cookie_name + '=([^;]*)(;|$)');

  if (results) {
    return (unescape(results[2]));
  } else {
    return null;
  }
}

// Need this before we start using Google API calls.
google.load("maps", "2.x");
var prefix = "./includes/vignette.php?img=.";
var options = "&bgc=ffffff&d=10&f=20&b=0&c=1";

// Call this function when the page has been loaded
function initialize() {
  saddr = get_cookie("start_address");
  if(saddr == null) {
    saddr = "";
  }
  if (!GBrowserIsCompatible()) {
    alert("Sorry, the Google Maps API is not compatible with this browser");
  } else {
    // Read in towers from an XML definition file.
    var request = GXmlHttp.create();
    request.open("GET", "towers.xml", true);
    request.onreadystatechange = function() {
      if (request.readyState == 4) {
        var xmlDoc = request.responseXML;
        var towers = xmlDoc.documentElement.getElementsByTagName("tower");
        initGoogleMaps(towers);
        initOSMaps(towers);
      }
    }
    request.send(null);
  }
}
google.setOnLoadCallback(initialize);

/*** Google Maps
  Google Reference: http://code.google.com/apis/maps/documentation/mapplets/reference.html
  Useful Google Maps reference: http://econym.googlepages.com/
***/

// Must be declared at the top level and remain visible to javascript calls in HTML.
var googleMarkers = [];
var googleDirBoxes = [];
var googleMap;
var gdir;
var saddr = "";
var contextmenu;
var clickedPixel;
// Store these values for use with the return to "Cheltenham Branch" link above the map.
var cheltCentre;
var cheltGoogleZoom;

function initGoogleMaps(towers) {
  function getOptions(tower) {
    return {
      icon: icons[parseInt(tower.getAttribute("bells"))],
      draggable: false,
      bouncy: false,
      title: tower.getAttribute("town")};
  }

  function createMarker(tower) {
    var latlong = new GLatLng(parseFloat(tower.getAttribute("latitude")), parseFloat(tower.getAttribute("longitude")));
    var marker = new GMarker(latlong, getOptions(tower));
    bounds.extend(latlong);
    var infoBox = "<h4>" + tower.getAttribute("town") + "</h4>";
    var photo = tower.getElementsByTagName("photo")[0];
    if (photo != null) {
      var thumb = photo.getElementsByTagName("thumbnail")[0];
      var full = photo.getElementsByTagName("fullsize")[0];
      var imgLink = '<a href="' + photo.getElementsByTagName("fullsize")[0].firstChild.nodeValue + '" rel="lightbox" title="' + tower.getAttribute("dedication") + ', ' + tower.getAttribute("town") + ' taken by ' + photo.getElementsByTagName("photographer")[0].firstChild.nodeValue.htmlEntities() + '" onclick="pop(this);">';
      infoBox += '<p class="small">' + imgLink;
      infoBox += '<img align="top" src="' + prefix + thumb.firstChild.nodeValue + options + '" alt="Taken by ' + photo.getElementsByTagName("photographer")[0].firstChild.nodeValue.stripTags() + '" border="0"';
      // Specification of the image height parameter prevents poor layout in Google popups
      // on the first click when the image dimensions are still unknown
      if (thumb.getAttribute("height")) {
        infoBox += ' height="' + (parseInt(thumb.getAttribute("height")) + 20) + '"';
      }
      if (thumb.getAttribute("width")) {
        infoBox += ' width="' + (parseInt(thumb.getAttribute("width")) + 20) + '"';
      }
      infoBox += '></a></p>';
    }
    infoBox += '<p class="plain">' + tower.getAttribute("dedication");
    infoBox += "<br>" + tower.getAttribute("bells") + ", " + tower.getAttribute("weight") + ", " + tower.getAttribute("osgrid") + "</p>";
    infoBox += '<p class="small">';
    infoBox += imgLink + 'Picture</a>, ';
    infoBox += '<a href="javascript:toTower(' + nextMarker + ')">Directions</a>, ';
    infoBox += '<a href="javascript:openOSInfoBox(' + nextMarker + ')">OS Map</a>, ';
    infoBox += '<a href="javascript:openGoogleCloseUp(' + nextMarker + ')">Magnify</a>, ';
    infoBox += '<a href="#' + tower.getAttribute("ref") + '">More...</a>';
    infoBox += '</p>';

    var directionsBox = '<h4>Directions to ' + tower.getAttribute("town") + '</h4>';
    directionsBox += '<form action="javascript:getDirections(' + nextMarker + ')"><table>';
    directionsBox += '<tr><td>From: ';
    directionsBox += '<input type="text" size=30 maxlength=40 name="saddr" id="saddr" value=""></td></tr>';
    directionsBox += '<tr><td class=small align=right>(e.g. Swindon Road, Cheltenham)</td></tr>';
    directionsBox += '<tr><td align=center><input value="Get Directions" type="submit"></td></tr>';
    directionsBox += '<input type="hidden" id="daddr" value="' + tower.getAttribute("town") + ', ' + tower.getAttribute("dedication") + "@" + latlong.lat() + ',' + latlong.lng() + '"></table></form>';

    //marker.bindInfoWindowHtml(infoBox);

    GEvent.addListener(marker, "click", function() {
      marker.openInfoWindowHtml(infoBox);
    });
    GEvent.addListener(marker, "dblclick", function() {
      marker.showMapBlowup({zoomLevel: 18, mapType: G_SATELLITE_MAP});
    });

    googleMap.addOverlay(marker);
    googleMarkers[nextMarker] = marker;
    googleDirBoxes[nextMarker] = directionsBox;
    nextMarker++;
  }

  googleMap = new GMap2(document.getElementById("gmap"));
  gdir = new GDirections(googleMap, document.getElementById("directions"));
  var nextMarker = 0;

  // Array for decoding the failure codes
  // http://code.google.com/apis/maps/documentation/reference.html#GGeoStatusCode
  var reasons=[];
  reasons[G_GEO_SUCCESS]             = "Success";
  reasons[G_GEO_BAD_REQUEST]         = "A directions request could not be successfully parsed.";
  reasons[G_GEO_SERVER_ERROR]        = "Server error: The geocoding request could not be successfully processed.";
  reasons[G_GEO_MISSING_QUERY]       = "No query was specified in the input.";
  reasons[G_GEO_MISSING_ADDRESS]     = "Missing Address: The address was either missing or had no value.";
  reasons[G_GEO_UNKNOWN_ADDRESS]     = "Unknown Address: No corresponding geographic location could be found for the specified address.\nTry a more specific address, e.g. Tewkesbury, Gloucestershire.";
  reasons[G_GEO_UNAVAILABLE_ADDRESS] = "Unavailable Address: The geocode for the given address cannot be returned due to legal or contractual reasons.";
  reasons[G_GEO_UNKNOWN_DIRECTIONS]  = "Could not compute the directions.\nMake sure you have specified two points and that a path actually exists to be found.";
  reasons[G_GEO_BAD_KEY]             = "Bad Key: The API key is either invalid or does not match the domain for which it was given";
  reasons[G_GEO_TOO_MANY_QUERIES]    = "Too Many Queries: The daily geocoding quota for this site has been exceeded.";

  // Catch Directions errors
  GEvent.addListener(gdir, "error", function() {
    var code = gdir.getStatus().code;
    var reason = "Code " + code;
    if (reasons[code]) {
      reason = reasons[code]
    }
    alert("Failed to obtain directions, " + reason);
  });

  // Only make the directions visible if they were successfully derived.
  GEvent.addListener(gdir, "load", function() {
    document.getElementById("directions_text").style.display = "block";
  });

  googleMap.addControl(new GLargeMapControl());
  googleMap.addControl(new GMapTypeControl());
  googleMap.addControl(new GScaleControl());
  googleMap.addControl(new GOverviewMapControl(new GSize(160, 90)));
  // Cheltenham town centre
  googleMap.setCenter(new GLatLng(51.901144, -2.076727), 10);
  // Works, but the map and web page both scroll at the same time.
  // Not true in Firefox.
  googleMap.enableScrollWheelZoom();

  // Create the required icons
  var iconSize = new GSize(24, 24);
  var iconAnchor = new GPoint(12, 12);
  var infoWindowAnchor = new GPoint(12, 0);
  var icons = [];

  for(i = 0; i <= 12; i++) {
    switch (i) {
      case 0:
      case 1:
      case 2:
      case 7:
      case 9:
      case 10:
      case 11:
        icons[i] = new GIcon();
        icons[i].image = "./graphics/bellx.gif";
        icons[i].iconSize = iconSize;
        icons[i].iconAnchor = iconAnchor;
        icons[i].infoWindowAnchor = infoWindowAnchor;
        break
      default:
        icons[i] = new GIcon();
        icons[i].image = "./graphics/bell" + i + ".gif";
        icons[i].iconSize = iconSize;
        icons[i].iconAnchor = iconAnchor;
        icons[i].infoWindowAnchor = infoWindowAnchor;
    }
  }

  // Add locations
  var bounds = new GLatLngBounds();

  // Perform actions for each tower
  for (var i = 0; i < towers.length; i++) {
    createMarker(towers[i]);
  }
  cheltCentre = bounds.getCenter();
  cheltGoogleZoom = googleMap.getBoundsZoomLevel(bounds);
  googleMap.setCenter(cheltCentre, cheltGoogleZoom);

  // Create the context menu div
  contextmenu = document.createElement("div");
  contextmenu.style.visibility="hidden";
  contextmenu.style.background="#ffffff";
  contextmenu.style.border="1px solid #8888FF";

  contextmenu.innerHTML = '<a href="javascript:startHere()"><div class="context">&nbsp;&nbsp;Start Journey here&nbsp;&nbsp;</div></a>'
                        + '<hr width=130>'
                        + '<a href="javascript:googleZoomInHere()"><div class="context">&nbsp;&nbsp;Zoom in here&nbsp;&nbsp;</div></a>'
                        + '<a href="javascript:googleZoomOutHere()"><div class="context">&nbsp;&nbsp;Zoom out here&nbsp;&nbsp;</div></a>'
                        + '<a href="javascript:googleCentreMapHere()"><div class="context">&nbsp;&nbsp;Centre map here&nbsp;&nbsp;</div></a>';

  googleMap.getContainer().appendChild(contextmenu);

  GEvent.addListener(googleMap, "singlerightclick", function(pixel, tile) {
    // 1. Store the "pixel" info in case we need it later
    // 2. Adjust the context menu location if near an edge
    // 3. Create a GControlPosition
    // 4. Apply it to the context menu, and make the context menu visible
    clickedPixel = pixel;
    var x = pixel.x;
    var y = pixel.y;
    if (x > googleMap.getSize().width - 140) {
      x = googleMap.getSize().width - 140
    }
    if (y > googleMap.getSize().height - 80) {
      y = googleMap.getSize().height - 80
    }
    var pos = new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(x, y));
    pos.apply(contextmenu);
    contextmenu.style.visibility = "visible";
  });

  // If the user clicks on the map, close the context menu
  GEvent.addListener(googleMap, "click", function() {
    contextmenu.style.visibility = "hidden";
  });

  window.onunload = GUnload;
}

function openGoogleInfoBox(i) {
  osMap.closeInfoWindow();
  if (GBrowserIsCompatible()) {
    showGoogleMap();
    GEvent.trigger(googleMarkers[i], "click");
  }
}

function openGoogleCloseUp(i) {
  osMap.closeInfoWindow();
  if (GBrowserIsCompatible()) {
    showGoogleMap();
    GEvent.trigger(googleMarkers[i], "dblclick");
  }
}

/** Google driving directions **/
function getDirections(i) {
  osMap.closeInfoWindow();
  if (GBrowserIsCompatible()) {
    saddr = document.getElementById("saddr").value
    var daddr = document.getElementById("daddr").value
    set_cookie("start_address", saddr);
    gdir.load("from: " + saddr + " to: " + daddr);
    googleMarkers[i].closeInfoWindow();
  }
}

function clearDirections() {
  osMap.closeInfoWindow();
  if (GBrowserIsCompatible()) {
    // In order to close all and any information boxes they must be disabled and then re-enabled.
    googleMap.disableInfoWindow();
    googleMap.enableInfoWindow();
    gdir.clear();
    document.getElementById("directions_text").style.display = "none";
  }
}

function toTower(i) {
  osMap.closeInfoWindow();
  if (GBrowserIsCompatible()) {
    showGoogleMap();
    googleMarkers[i].openInfoWindowHtml(googleDirBoxes[i]);
    document.getElementById("saddr").value = saddr;
  }
}

function startHere() {
  if (GBrowserIsCompatible()) {
    var point = googleMap.fromContainerPixelToLatLng(clickedPixel)
    googleMap.setCenter(point);
    contextmenu.style.visibility = "hidden";
    saddr = "Chosen Location@" + point.lat() + "," + point.lng();
    // In order to close all and any information boxes they must be disabled and then re-enabled.
    googleMap.disableInfoWindow();
    googleMap.enableInfoWindow();
  }
}

/** Context menu options **/
function googleZoomInHere() {
  if (GBrowserIsCompatible()) {
    var point = googleMap.fromContainerPixelToLatLng(clickedPixel)
    googleMap.zoomIn(point, true);
    contextmenu.style.visibility = "hidden";
  }
}

function googleZoomOutHere() {
  if (GBrowserIsCompatible()) {
    var point = googleMap.fromContainerPixelToLatLng(clickedPixel)
    googleMap.setCenter(point, googleMap.getZoom() - 1); // There is no googleMap.zoomOut() equivalent
    contextmenu.style.visibility = "hidden";
  }
}

function googleCentreMapHere() {
  if (GBrowserIsCompatible()) {
    var point = googleMap.fromContainerPixelToLatLng(clickedPixel)
    googleMap.setCenter(point);
    contextmenu.style.visibility = "hidden";
  }
}

/*** Ordanance Survey Maps ***
  Javascript XML Parsing: http://binodsuman.blogspot.com/
  OpenLayers: http://dev.openlayers.org/docs/files/OpenLayers/Layer/Markers-js.html#OpenLayers.Layer.Markers.OpenLayers.Layer.Markers
  OpenSpace: http://openspace.ordnancesurvey.co.uk/openspace/OpenSpaceAPIDocs0.8.0/files/OpenSpace/Map-js.html#OpenSpace.Map.createMarker
  OpenSpace examples to get started: http://openspace.ordnancesurvey.co.uk/openspace/support.html
***/
var osMap;
var gridProjection = new OpenSpace.GridProjection();
var OSMarkers = [];

function initOSMaps(towers) {
  osMap = new OpenSpace.Map('osmap', {
    /* Reassign this as the OpenLayers._getScriptLocation() function has been overridden
       in the off-line version since the opensource.js file was retrieved from the Internet. */
    theme: OpenLayers._getScriptLocation() + 'theme/openspace/style.css'
    });
  var nextMarker = 0;
  var control = new OpenSpace.Control.OverviewMap();
  osMap.addControl(control);
  control.maximizeControl();

  // First loop through the array of towers to get the bounds of the map.
  var lonlat = [], pos = [];
  var bounds = new OpenSpace.MapBounds();
  for (var i = 0; i < towers.length; i++) {
    lonlat[i] = new OpenLayers.LonLat(parseFloat(towers[i].getAttribute("longitude")), parseFloat(towers[i].getAttribute("latitude")));
    pos[i] = gridProjection.getMapPointFromLonLat(lonlat[i]);
    bounds.extend(pos[i]);
  }
  // Zoom and centre point
  osMap.setCenter(bounds.getCenterLonLat(), 5);
  // Add markers only *after* osMap.setCenter() call to avoid null pointer errors.
  for (var i = 0; i < towers.length; i++) {
    // Cater for the largest expect dimensions of the photo in the information box.
    var h = 150, w = 150;
    var infoBox = "<h4>" + towers[i].getAttribute("town") + "</h4>";
    var photo = towers[i].getElementsByTagName("photo")[0];
    if (photo != null) {
      var thumb = photo.getElementsByTagName("thumbnail")[0];
      var full = photo.getElementsByTagName("fullsize")[0];
      var imgLink = '<a href="' + photo.getElementsByTagName("fullsize")[0].firstChild.nodeValue + '" rel="lightbox" title="' + towers[i].getAttribute("dedication") + ', ' + towers[i].getAttribute("town") + ' taken by ' + photo.getElementsByTagName("photographer")[0].firstChild.nodeValue.htmlEntities() + '" onclick="pop(this);">';
      infoBox += '<p class="small">' + imgLink;
      infoBox += '<img align="top" src="' + prefix + thumb.firstChild.nodeValue + options + '" alt="Taken by ' + photo.getElementsByTagName("photographer")[0].firstChild.nodeValue.stripTags() + '" border="0"';
      // Specification of the image height parameter prevents poor layout in Google popups
      // on the first click when the image dimensions are still unknown
      if (thumb.getAttribute("height")) {
        h = parseInt(thumb.getAttribute("height"));
        infoBox += ' height="' + (h + 20) + '"';
      }
      if (thumb.getAttribute("width")) {
        w = parseInt(thumb.getAttribute("width"));
        infoBox += ' width="' + (w + 20) + '"';
      }
      infoBox += '></a></p>';
    }
    infoBox += '<p class="plain">' + towers[i].getAttribute("dedication");
    infoBox += "<br>" + towers[i].getAttribute("bells") + ", " + towers[i].getAttribute("weight") + ", " + towers[i].getAttribute("osgrid") + "</p>";
    infoBox += '<p class="small">';
    infoBox += imgLink + 'Picture</a>, ';
    // The HTML links in these OS infoBoxes do not always work in IE. Moved Javascript to "onclick" and added an href to fix it.
    infoBox += '<a onclick="javascript:openGoogleInfoBox(' + i + ')" href="#mapgraphics">Google Map</a>, ';
    // The HTML links in these OS infoBoxes do not always work in IE. Added a Javascript hack to compensate.
    infoBox += '<a onclick="javascript:goToTag(\'' + towers[i].getAttribute("ref") + '\')" href="#' + towers[i].getAttribute("ref") + '">More...</a>';
    infoBox += '</p>';
    OSMarkers[nextMarker++] = osMap.createMarker(pos[i], OSBellIcon(parseInt(towers[i].getAttribute("bells"))), infoBox, new OpenLayers.Size(140 + w, 190 + h));
  }
}

/* Return an icon to represent a tower based on the number of bells it has.
   Each icon can be used no more than once. Hence need to return a new icon each time. */
function OSBellIcon(bells) {
  var size = new OpenLayers.Size(24, 24);
  var offset = new OpenLayers.Pixel(-size.w / 2, -size.h / 2);
  switch (bells) {
    case 0:
    case 1:
    case 2:
    case 7:
    case 9:
    case 10:
    case 11:
      return new OpenSpace.Icon('./graphics/bellx.gif', size, offset);
      break
    default:
      return new OpenSpace.Icon('./graphics/bell' + bells + '.gif', size, offset);
  }
}

function openOSInfoBox(marker) {
  if (GBrowserIsCompatible()) {
    googleMap.closeInfoWindow();
  }
  showOSMap();
  osMap.getMarkerLayer().markers[marker].events.triggerEvent("mousedown");
}

/*** Inter-map functions ***/
function scrollGoogleMap(lat, long, zoom) {
  if (GBrowserIsCompatible()) {
    googleMap.closeInfoWindow();
    googleMap.setCenter(new GLatLng(lat, long), zoom);
    showGoogleMap();
  }
}

function scrollOSMap(lat, long, zoom) {
  if (GBrowserIsCompatible()) {
    googleMap.closeInfoWindow();
  }
  osMap.closeInfoWindow();
  osMap.setCenter(gridProjection.getMapPointFromLonLat(new OpenLayers.LonLat(long, lat)), zoom);
  showOSMap();
}

function syncUpOSMap() {
  if(googleMap.isLoaded()) {
    osMap.closeInfoWindow();
    if (GBrowserIsCompatible()) {
      googleMap.closeInfoWindow();
      // Get position of Google Map
      var c = googleMap.getCenter();
      // Centre OS Map
      scrollOSMap(c.lat(), c.lng(), googleMap.getZoom() - 6);
      showOSMap();
    }
  }
}

function syncUpGoogleMap() {
  // Get position of OS Map
  var c = gridProjection.getLonLatFromMapPoint(osMap.getCenter());
  osMap.closeInfoWindow();
  if (GBrowserIsCompatible()) {
    googleMap.closeInfoWindow();
    // Centre Google Map
    scrollGoogleMap(c.lat, c.lon, osMap.getZoom() + 6);
    showGoogleMap();
  }
}

function showGoogleMap() {
  var gmap = document.getElementById("gmap");
  var ginstr = document.getElementById("googleinstructions");
  var osmap = document.getElementById("osmap");
  if(gmap) {
    gmap.style.display = "block";
  }
  if(ginstr) {
    ginstr.style.visibility = "visible";
  }
  if(osmap) {
    osmap.style.display = "none";
  }
  var gmapbutton = document.getElementById("googlemapbutton");
  gmapbutton.className = "selectedmap";
  var osmapbutton = document.getElementById("osmapbutton");
  osmapbutton.className = "unselectedmap";
}

function showOSMap() {
  var gmap = document.getElementById("gmap");
  var ginstr = document.getElementById("googleinstructions");
  var gdirbox = document.getElementById("directions_text");
  var osmap = document.getElementById("osmap");
  if(gmap) {
    gmap.style.display = "none";
  }
  if(ginstr) {
    ginstr.style.visibility = "hidden";
  }
  if(gdirbox) {
    gdirbox.style.display = "none";
  }
  if(gdir) {
    gdir.clear();
  }
  if(osmap) {
    osmap.style.display = "block";
  }
  var gmapbutton = document.getElementById("googlemapbutton");
  gmapbutton.className = "unselectedmap";
  var osmapbutton = document.getElementById("osmapbutton");
  osmapbutton.className = "selectedmap";
}

function moveCheltenham() {
  var gmap = document.getElementById("gmap");
  if (gmap && (gmap.style.display == "block")) {
    // Google map showing
    scrollGoogleMap(cheltCentre.lat(), cheltCentre.lng(), cheltGoogleZoom);
  } else {
    // OS map showing
    scrollOSMap(cheltCentre.lat(), cheltCentre.lng(), cheltGoogleZoom - 6);
  }
}

/*
onmouseover="javascript:selectButton()"
onmouseout="javascript:unselectButton()"

function selectButton() {
  var centrebutton = document.getElementById("centrebutton");
  centrebutton.className = "selectedmap";
}

function unselectButton() {
  var centrebutton = document.getElementById("centrebutton");
  centrebutton.className = "unselectedmap";
}
*/

