/* Copyright (c) 2006-2007 David Sheldon and Nick Burch
   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
   IN THE SOFTWARE.
*/

/* File: mapTiles.js
*/
var size;   // Size of array grid in each dimension
var data;   // 2 dimensional array to hold the grid data

var sizeGridX = 6;   // Size of visible grid (table) in each dimension
var sizeGridY = 4;   // Size of visible grid (table) in each dimension

var minY = -86; // Minimum Y at scale 1

var tileWidth = 125;
var tileHeight = 125;

var markerXOffset = 4;
var markerYOffset = 11;

var offsetGridX = 450;
var offsetGridY = 207;

var zoomLevel = 1;
var grid = 'osgb'; // One of osgb or osie, for now anyway
var version = 'latest'; // Either "latest" or a decade eg "1920s"

var needCalibration = true;

var prefixes = new Array(1);
var root1 = "http://ustile.npemap.org.uk/";
var root2 = "http://tile.npemap.org.uk/";
var root = "http://tile.npemap.org.uk/";

prefixes[1] =  "scaled1/";
prefixes[2] =  "scaled3/";
prefixes[3] =  "scaled6/";

var jumpOut = new Array(1);
jumpOut[1] = 3;
jumpOut[2] = 2;
var jumpIn = new Array(1);
jumpIn[2] = 3;
jumpIn[3] = 2;

var zooms = new Array(1);
zooms[1] = 1;
zooms[2] = 3;
zooms[3] = 6;

var sources = new Array(1);
sources[0] = 'this site';
sources[1] = '<a href="http://www.freethepostcode.org/">freethepostcode.org</a>';
sources[2] = '<a href="http://www.npemap.org.uk">npemap</a>';
sources[3] = '<a href="http://www.dracos.co.uk/play/locating-postboxes/">dracos postboxes</a>';

var screenWidth = 0;
var screenHeight = 0;
var bottomLeft = new Array();


var markerElement;
var postcodeFormElement;
var mainPageElement;


// Postcode specific helper functions
function box1Keypress(e) {
  var event = YAHOO.util.Event.getEvent(e);
  var key = String.fromCharCode(YAHOO.util.Event.getCharCode(event));
  if (key == ' ') {
    $('postcode2').focus();
    return false;
  } else if (key >= '0' && key <= '9' && this.value.length == 3) {
    this.value = this.value + key;
    $('postcode2').focus();
    YAHOO.util.Event.preventDefault(event);
  }
}


YAHOO.util.Event.addListener('postcode1', 'keypress', box1Keypress );


function showPostcodeWindow(easting, northing, x, y) {
    hideMarkerWindow();
    if(postcodeFormElement == null) {
       // Decade based view lacks a postcode submit form
       return;
    }

    var easting = trimSixDigits(Math.round(easting));
    var northing = trimSixDigits(Math.round(northing));

    markerElement.style.visibility = 'visible';
    YAHOO.util.Dom.setXY(markerElement, [(x-markerXOffset), (y-markerYOffset)]);
    
    postcodeFormElement.style.display = 'block';
    YAHOO.util.Dom.setXY(postcodeFormElement, [Math.max(0, (x-50)), (y-180) < 0 ? (y+10):(y-180)]);
    
   // Compute lat+long
   var en = new OSRef(easting,northing);
   var latlong = en.toLatLng();
   var lat = Math.round(latlong.lat*10000) / 10000;
   var lng = Math.round(latlong.lng*10000) / 10000;
   

    $('location').innerHTML = easting + ', ' + northing + " (" + lat + ", " + lng + ")";
    $('easting').value = easting;
    $('northing').value = northing;
    $('returnX').value = offsetGridX;
    $('returnY').value = offsetGridY;
    $('grid').value = grid;
    $('grid2').value = grid;

    $('postcode1').value = "";
    $('postcode2').value = "";
    $('postcode1').focus();

}


YAHOO.util.Event.addListener('postcode_submit', 'submit', submitPostcodeAjax);

function submitPostcodeAjax(e) {
  var el = $('postcode_submit');
  var data = Form.serialize(el);
  Element.show('status');
  $('status').innerHTML = "<p>Submitting postcode "+ $F('postcode1') + " " + $F('postcode2') + "...</p>";
  if (typeof urchinTracker == 'function') urchinTracker('/tiles/submitPostcode');
 new Ajax.Request(el.getAttribute("action"), {
           method:"post",
           postBody:data, 
           onComplete: function(o) {drawExisting(); updateStatus(o);  }
 });
 closePostcodeWindow();
 YAHOO.util.Event.preventDefault(e);
}

var updateStatus = function(o) {
  $('status').innerHTML = o.responseText; 
}



function closePostcodeWindow() {
  if(postcodeFormElement != null) {
     postcodeFormElement.style.display = 'none';
     markerElement.style.visibility = 'hidden';
  }
}

function showMarkerWindow(x,y, postcode, easting, northing, source, id) {
  closePostcodeWindow();
   // Compute lat+long
   var en = new OSRef(easting,northing);
   var latlong = en.toLatLng();
   var lat = Math.round(latlong.lat*10000) / 10000;
   var lng = Math.round(latlong.lng*10000) / 10000;

   // Display
   $('location_detail').innerHTML = easting + ", "+northing;
   $('latlong_detail').innerHTML = lat + ", " + lng;
   $('postcode_detail').innerHTML = postcode;
   $('postcode_id_detail').value= id; 
   $('reason_email').value= ''; 
   $('reason').value= ''; 
   $('grid').value = grid;
   $('grid2').value = grid;
   $('source_detail').innerHTML = sources[source];
   $('postcode_detail_window').style.display = 'block';
   $('bad').style.display = '';
   $('report_form').style.display = 'none';
   YAHOO.util.Dom.setXY('postcode_detail_window', [Math.max(0, (x-50)), (y-100) < 0 ? (y+10):(y-100)]);
}

function hideMarkerWindow() {
   var pdw = $('postcode_detail_window');
   if(pdw != null ) {
       pdw.style.display = 'none';
   }
}


// MAP METHODS --------------------------------------------

function mapClick(e) {
    e = YAHOO.util.Event.getEvent(e);
    var img = YAHOO.util.Event.getTarget(e);
    var x = YAHOO.util.Event.getPageX(e);
    var y = YAHOO.util.Event.getPageY(e);
    var imgPos = YAHOO.util.Dom.getXY(img);
    var tileX = x - imgPos[0];
    var tileY = y - imgPos[1];
    // Tiles are eee/nnn.jpg
    var tile = img.src.substring( img.src.lastIndexOf("/") - 3 );
    var baseEasting = tile.substring(0, tile.indexOf("/"))-0;
    var baseNorthing = tile.substring(tile.indexOf("/") + 1, tile.indexOf("."))-0;
    var tileXratio =  tileX/img.offsetWidth;
    var tileYratio = 1 -(tileY/img.offsetHeight);
    if (zoomLevel != 1) {
      // Zoom in.
      gotoLocation(Math.round((baseEasting -0 + tileXratio) * zooms[zoomLevel]), 
			             Math.round((baseNorthing -0 + tileYratio) *  zooms[zoomLevel]),
									  zoomLevel - 1);
    } else {
    
      showPostcodeWindow(((baseEasting  + tileXratio)*1000), ((baseNorthing +tileYratio)*1000), x, y); 

    }
  }

YAHOO.util.Event.addListener('map', 'click', mapClick);



function zoomOut() {
	if(zoomLevel == (prefixes.length-1)) {
    location.href="allmaps.html";
		return;
	}
	tidyUp();

  gotoLocation(offsetGridX * zooms[zoomLevel], offsetGridY * zooms[zoomLevel], zoomLevel + 1);
}

function tidyUp() {
  removeMarkers();
}

function zoomIn() {
	if(zoomLevel == 1) {
		return;
	}

  gotoLocation(offsetGridX * zooms[zoomLevel], offsetGridY * zooms[zoomLevel], zoomLevel - 1);
}

function fixZoomButton() {
  if(zoomLevel == 1) {
    Element.hide('zoomin_enabled');
    Element.show('zoomin_disabled');
    Element.show('showHideMarkers');
  } else {
    Element.show('zoomin_enabled');
    Element.hide('zoomin_disabled');
    Element.hide('showHideMarkers');
  }
}

function doResize() {
  var newWidth = YAHOO.util.Dom.getViewportWidth();
  var newHeight = YAHOO.util.Dom.getViewportHeight();
  if (screenHeight != newHeight || screenWidth != newWidth) {
		screenWidth = YAHOO.util.Dom.getViewportWidth();
		screenHeight = YAHOO.util.Dom.getViewportHeight();
		drawTable();
		refreshGrid();
	}

}

function drawTable() {
  sizeGridX = Math.floor(((screenWidth -210) / tileWidth));
  sizeGridY = Math.floor(((screenHeight -130) / tileHeight));
  var map = $('map');
  map.innerHTML = '';
  for(var y=sizeGridY; y>=1; y--) {
    for(var x=1; x<=sizeGridX; x++) {
			map.appendChild(createIMG(tileWidth, tileHeight, 'element'+x+'.'+y));
     }
		 map.appendChild(document.createElement('br')); 
  }
  
  $('map').style.width = (sizeGridX * tileWidth) + "px" ;
}

function createIMG(width, height, id) {
  var img = document.createElement('img');
	img.width = width;
	img.height = height;
	img.id = id;
	return img;
}


function setVersion(tileVersion) {
    if(tileVersion != null) {
       version = tileVersion;
    }
}

function initializePage(skipMarkers, tileVersion) {
    markerElement = $('marker');
    postcodeFormElement = $('postcode_form');
    mainPageElement = $('main_page');

    if(tileVersion != null) {
       version = tileVersion;
    }

	if(skipMarkers != null && skipMarkers) {
	} else {
		updateShowMarkers();
	}
    parseQueryString();
    fixZoomButton();

    doResize();
}

function reload_page() {
    parseQueryString();
    fixZoomButton();
    refreshGrid();
}
  

function parseQueryString() {
  var hash = location.href.lastIndexOf('#');
	var query = location.href.lastIndexOf("?")
  var params = '';
  if (hash != -1) {
    params = location.href.substring(hash+1);
  }
  else if (query != -1) {
    params = location.href.substring(query+1);
  }
  if (params.length > 0) {
    urlterms=params.split(",")
    offsetGridX = urlterms[0]-0 ; 
    offsetGridY = urlterms[1]-0 ; 

    if(urlterms.length > 2) {
      zoomLevel = urlterms[2]-0 ;
    }

    grid = 'osgb';
    if(urlterms.length > 3) {
      if(urlterms[3] == 'ie') {
         grid = 'osie';
      }
    }
  }
}

function gotoLocation(easting, northing, zoom) {
  offsetGridX = Math.round(easting / zooms[zoom]); 
  offsetGridY = Math.round(northing/ zooms[zoom]); 
  zoomLevel = zoom;
  fixZoomButton();
  refreshGrid();
}


// Need to use substring for IE
// Pads to 3 digits with zeros, or - and 2 digits
function trimNumber(string) {
	// If we have a 3+ digit number, no need to do any padding
	if(parseInt(string) >= 100) { return string; }
	// Or a 2+ digit negative number, no need to do any padding
	if(parseInt(string) <= -10) { return string; }

	// Special case for 1 digit negative numbers
	if(parseInt(string) <= 0) {
		var num = parseInt(string);
		return '-0' + (0-num);
	}

	// Pad it so that it'll always have leading 0s, and be at least 3 long
	str = '000' + string
	// Grab the right most 3 digits, which will have any required padding
	return str.substring(str.length -3)
}

function trimSixDigits(string) {
	// If we have a 6+ digit number, no need to do any padding
	if(parseInt(string) >= 100000) { return string; }
	// Or a 5+ digit negative number, no need to do any padding
	if(parseInt(string) <= -10000) { return string; }

	// Is it negative?
	var num = parseInt(string)
	var isNegative = (num < 0);
	if(isNegative) {
		num = 0 - num;
	}

	// Pad it so that it'll always have leading 0s, and be at least 6 long
	str = '000000' + num;
	// Grab the right most 6 digits, which will have any required padding
	str = str.substring(str.length -6)
	// Add back in - at start if needed
	if(isNegative) {
		str = '-' + str.substring(1);
	}
	return str;
}

function refreshGrid() {
  closePostcodeWindow();
  hideMarkerWindow();

	for(var x=1; x<=sizeGridX; x++) {
		for(var y=1; y<=sizeGridY; y++) {
            tileX = x+offsetGridX-Math.round(sizeGridX/2);
            tileY = y+offsetGridY-Math.round(sizeGridY/2);
            $('element' + x + '.' + y).src = tileURL(tileX, tileY);
        }
    }
  minEasting = (offsetGridX-Math.round(sizeGridX/2))+1;
  minNorthing= (offsetGridY-Math.round(sizeGridY/2))+1;
  maxEasting = minEasting + sizeGridX;
  maxNorthing= minNorthing + sizeGridY;

  if (zoomLevel == 1) {
		drawExisting();
	}
	updatePermalink();
	updateSheets(minEasting,minNorthing,maxEasting,maxNorthing);
}

function tileURL(tileX, tileY) {
	  //var root = (tileX % 2) == 0 ? root1 : root2;
		return root + grid + '/' + version + '/' + prefixes[zoomLevel] + trimNumber(tileX)+'/'+ trimNumber(tileY) + '.jpg';
}

function drawExisting() {
	if (shouldWeShowMarkers()) {
		var url = "/cgi/get-postcodes.fcgi";
		var params = "mineasting=" + minEasting + "000&maxeasting="  + maxEasting + "000&minnorthing=" + minNorthing + "000&maxnorthing=" + maxNorthing+"000" + "&grid=" + grid;
		
		removeMarkers();
    $('spinner').innerHTML = "<p>Loading....</p>";
		needCalibration = true;
		
    var myAjax = new Ajax.Request(url, {
        method: 'get',
        parameters: params,
        onComplete: successHandler,
        onFailure: failHander
    });
        
  }
}
var successHandler = function(o){ 
  eval(o.responseText);  
}

var failHander = function(o) {
  // Don't do anything
  $('spinner').innerHTML = '<p class="error">Problem showing existing data.</p>';
}

function findMarkerOffsets() {
  newMarker = document.createElement('div');
  newMarker.style.position = 'absolute';
  mainPageElement.appendChild(newMarker);

  topLeft = YAHOO.util.Dom.getXY('element1.' + sizeGridY);
  var bottom = topLeft[1] + (tileHeight*sizeGridY);
 
  YAHOO.util.Dom.setXY(newMarker, [topLeft[0] - markerXOffset, bottom - markerYOffset]);
  

  markerLeft = parseInt(newMarker.style.left);
  markerTop= parseInt(newMarker.style.top);
  mainPageElement.removeChild(newMarker);
	needCalibration = false;
}

function completeMarkers() {
	$('spinner').innerHTML = "&nbsp;";
}


var markers = new Array();
var nextMarker = 0;

function removeMarkers() {
  var pageElement = $('main_page');
  for(var i=0; i<nextMarker; i=i+1) {
    markers[i].style.visibility = 'hidden';
  }
  markerElement.style.visibility = 'hidden';
  nextMarker = 0;
}

var markerColours = new Array(1);
markerColours[0] = "../images/green-marker.gif";
markerColours[1] = "../images/blue-marker.gif";
markerColours[2] = "../images/green-marker.gif";
markerColours[3] = "../images/pink-marker.gif";


function addMarker(postcode, easting, northing, others ) {
  // Others is an array for us to put extra stuff in

  var newMarker;
  var id = others[0];
  var source = others[1];

  if (needCalibration) findMarkerOffsets();

  if (nextMarker < markers.length) {
    newMarker = markers[nextMarker]; 
  	var img=newMarker.firstChild;
    newMarker.style.visibility = 'visible';
  }
  else {
    newMarker = document.createElement('div');
  	var img=document.createElement('img');
    newMarker.appendChild(img);
    newMarker.className = 'postcode_marker';
    markers[markers.length] = newMarker;
    mainPageElement.appendChild(newMarker);
  }
  img.title = postcode;
  img.src = markerColours[source]; 

  nextMarker = nextMarker + 1;


  var x = ((easting / 1000) - minEasting) * tileWidth;
  var y = ((northing / 1000) - minNorthing) * tileHeight;
  
  newMarker.onclick = function(e) { 
    e = YAHOO.util.Event.getEvent(e);
    var x = YAHOO.util.Event.getPageX(e);
    var y = YAHOO.util.Event.getPageY(e);
    showMarkerWindow(x,y, postcode, easting, northing, source, id); 
  };

//  YAHOO.util.Dom.setXY(newMarker, [bottomLeft[0]+x-markerXOffset, bottom- (y+markerYOffset)]);
  setMarkerXY(newMarker, x, y);
}

function setMarkerXY(marker, x, y) {
  marker.style.left = Math.round(markerLeft + x) + 'px' ;
  marker.style.top = Math.round(markerTop - y) + 'px';
  
}


function updatePermalink() {
  $("permalink").href = "#" + getLink();
  if( $("latestMapLink") != null ) {
     $("latestMapLink").href = "/tiles/map.html#" + getLink();
  }
  location.href = '#'+getLink();
}

function updateSheets(minEasting,minNorthing,maxEasting,maxNorthing) {
	$("sheets-title").style.display = "none";
	$("sheets").style.display = "none";

	maxEasting -= 1;
	maxNorthing -= 1;

	if (zoomLevel == 1) {
		// Get list to request
		var req = "tiles=" + 
			minEasting + "x" + minNorthing + "," +
			minEasting + "x" + maxNorthing + "," +
			maxEasting + "x" + minNorthing + "," +
			maxEasting + "x" + maxNorthing ;
		if(grid == 'osie') {
			req = req + "&ie=yes";
		}

		// Do the request
		var url = "/cgi/get-meta.fcgi";
		var params = "format=js&callback=showMeta&" + req;
		var myAjax = new Ajax.Request(url, {
			method: 'get',
			parameters: params,
			onComplete: successHandler,
			onFailure: failHander
		});
	}
}

function showMeta(meta) {
	$("sheets-title").style.display = "";
	$("sheets").style.display = "";

	var count = 0;
	var tds = new Array(1);
	for( pos in meta ) {
		tds[count] = "<td>" + meta[pos]['sheet'];
		if( meta[pos]['year'] ) {
			var startLink = "";
			var endLink = "";
			if( meta[pos]['legend'] ) {
				startLink = "<a href=\"../legends/legend-" + meta[pos]['legend'] + ".jpg\" title=\"Legend for this map\">";
				endLink = "</a>";
			}

			tds[count] += " (" + startLink + meta[pos]['year'] + endLink + ")";
		}
		tds[count] += "</td>";
		count += 1;
	}
	// Order is min -> max easting, at same easting is min -> max northing
	$("sheets").innerHTML = "<table id='meta'><tr>" + tds[1] + tds[3] + "</tr><tr>" + tds[0] + tds[2] + "</tr></table>";
}

function getZoomOneLink() {
  return (offsetGridX * zooms[zoomLevel]) + "," + 
         (offsetGridY * zooms[zoomLevel]) + "," + 1;
}
function getLink() {
  var link = offsetGridX + "," + offsetGridY + "," + zoomLevel;
  if(grid == 'osie') {
     link = link + ",ie";
  }
  return link;
}


function updateGrid(theDirection) {
    if ( theDirection == 'right' ) {
        offsetGridX ++;
    }
    else if ( theDirection == 'left' ) {
        if ( offsetGridX > 0 )
            offsetGridX --;
    }
    else if ( theDirection == 'up' ) {
      offsetGridY ++;
    }
    else if ( theDirection == 'down' ) {
        if ( offsetGridY > Math.round(minY / zooms[zoomLevel]) )
            offsetGridY --;
    }
    else if ( theDirection == 'upleft' ) {
      offsetGridY ++;
      if ( offsetGridX > 0 )
          offsetGridX --;
    }
    else if ( theDirection == 'upright' ) {
      offsetGridY ++;
      offsetGridX ++; 
    }
    else if ( theDirection == 'downleft' ) {
      if ( offsetGridY > 0 )
        offsetGridY --; 
      if ( offsetGridX > 0 )
          offsetGridX --;
    }
    else if ( theDirection == 'downright' ) {
      if ( offsetGridY > 0 )
        offsetGridY --; 
      offsetGridX ++; 
    }

	refreshGrid();
}

function showReportForm() {
  $('report_form').style.display = 'block';
  $('bad').style.display = 'none';
  $('reason').focus(); 
  return false;
}

YAHOO.util.Event.addListener('bad_form', 'submit', submitBadPostcodeAjax);

function submitBadPostcodeAjax(e) {
  var el = $('bad_form');
  var data = Form.serialize(el);
  Element.show('status');
  $('status').innerHTML = "<p>Submitting bad postcode...</p>";
 new Ajax.Request(el.getAttribute("action"), {
           method:"post",
           postBody:data, 
           onComplete: updateStatus  
 });
  if (typeof urchinTracker == 'function') urchinTracker('/tiles/reportBad');
 hideMarkerWindow();
 YAHOO.util.Event.preventDefault(e);
}

YAHOO.util.Event.addListener('bad', 'click', showReportForm);

function handleArrowKeys(e) {
  var event = YAHOO.util.Event.getEvent(e);
  switch (event.keyCode) {
      case 37:
          updateGrid('left');
          break;    
      case 38:
          updateGrid('up');
          break;    
      case 39:
          updateGrid('right');
          break;    
      case 40:
          updateGrid('down');
          break;    
   }
}


function shouldWeShowMarkers() {
	if (readCookie('hideMarkers')) {
		return false;
	}
	return true;
}

function setMarkersHidden() {
  createCookie("hideMarkers", 'true', 30);
	removeMarkers();
	updateShowMarkers();
}

function unsetMarkersHidden() {
  eraseCookie("hideMarkers");
	drawExisting();
	updateShowMarkers();
}

function updateShowMarkers() {
  if (shouldWeShowMarkers()) {
    $('setMarkers').style.display = ''; 
    $('unsetMarkers').style.display = 'none'; 
	} else {
    $('setMarkers').style.display = 'none'; 
    $('unsetMarkers').style.display = ''; 
	}
}


function createCookie(name,value,days) {
	if (days) {
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		var expires = "; expires="+date.toGMTString();
	}
	else var expires = "";
	document.cookie = name+"="+value+expires+"; path=/";
}

function readCookie(name) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0;i < ca.length;i++) {
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
	}
	return null;
}

function eraseCookie(name) {
	createCookie(name,"",-1);
}
