var _locator_map;
var _locator_icon;
var _locator_performFiltering = false;
var _locator_geocoder;

var _locator_markers;
var _locator_messages;
var _locator_marker_locationIds;

// On document ready, initialize the map
$(document).ready(function() {
	_locator_initMap();
});

// helper functions
function _locator_isDefined(variable) {
	return (typeof (window[variable]) == "undefined") ? false : true;
}

/**
 * Decides whether haystack contains all elements of needle
 * @param haystack the array to search in
 * @param needle the array containing the items to look for
 * @return true iff ∀x ∈ needle: x ∈ haystack
 */
function _locator_containsAll(haystack, needle) {
	for(var item in needle) {
		
		if(haystack.indexOf(item) == -1) {
			return false;
		}
	}
	return true;
}

/**
 * Decides whether haystack contains any element of needle
 * @param haystack the array to search in
 * @param needle the array containing the items to look for
 * @return true iff ∃x ∈ needle: x ∈ haystack
 */
function _locator_containsAny(haystack, needle) {
	for(var item in needle) {
		
		if(haystack.indexOf(item) != -1) {
			return true;
		}
	}
	return false;
}


/**
 * Calculates the distance between two locations
 * @param loc1 the first location, with lat and long properties, standing for the latitude and longitude, in degrees. (eg {lat: 120, long: -30}).
 * @param loc2 the second location, with syntax like loc1.
 * @return the distance between the two locations in kilometers, assuming the locations are in the Netherlands.
 */
function _locator_calculateDistance(loc1, loc2) {
	var deltaY = (loc1.lat - loc2.lat) * (40074 / 360);
	var avgLat = (loc1.lat + loc2.lat) / 2;
	var deltaX = (loc1.long - loc2.long) * (40074 / 360)
			* Math.cos(Math.PI * avgLat / 180);

	var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
	return distance;
}

/**
 * Filters the locations and creates a top five of closest locations
 * @param refLoc the reference location, from which the distances have to be calculated
 * @param distance the maximum distance of the locations to the reference location
 * @return {filter: locationFilter, topFive: topFive}, in which locationFilter is an array of booleans indicating whether or not a location should be shown (locationFilter[i] == true indicates that locations[i] should be shown). topFive is an array of elements with distances and locations. topFive[2].distance is the distance of the 3rd element, topFive[1].locations represents the location of the 2nd element. 
 */
function _locator_filterLocations(refLoc, distance) {
	var locationFilter = new Array(locations.length);
	var deltaLat = (360 * distance / 40074);
	var deltaLong = deltaLat / Math.cos(Math.PI * refLoc.lat / 180);

	var topN = new Array(7); // change here for the top N locations
	for (i = 0; i < topN.length; i++) {
		topN[i] = {
			distance : 999
		};
	}

	for (i = 0; i < locations.length; i++) {
		var location = locations[i];
		
		if(location) {
			if (location.lat > (refLoc.lat + deltaLat)
					|| location.lat < (refLoc.lat - deltaLat)
					|| location.long > (refLoc.long + deltaLong)
					|| location.long < (refLoc.long - deltaLong)) {
				locationFilter[i] = false;
			} else {
				var calcDist = _locator_calculateDistance(location, refLoc);
				var filtered = (calcDist <= distance);
				if (filtered) {
					locationFilter[i] = true;
					var locationEntry = {
						distance : calcDist,
						location : location,
						index: i,
						id: location.id
					};
					_locator_insertIntoTopN(locationEntry, topN);
				}
			}
		}
	}
	return {
		filter : locationFilter,
		topN : topN
	};
}

/**
 * Takes a value consisting of a location and a distance and places it into the topFive, if necessary.
 * @param value the value to insert
 * @param topFive the topFive
 * @return void, the topFive is edited directly through reference.
 */
function _locator_insertIntoTopN(value, topN) {
	var i = 0;
	while (i < topN.length) {
		var ref = topN[i];
		if (value.distance < ref.distance)
			break;

		i++;
	}
	var temp = value;
	while (i < topN.length) {
		var temp2 = topN[i];
		topN[i] = temp;
		temp = temp2;
		i++;
	}
}

/**
 * Creates the correct icon for the zoom level and adds the correct markers
 * @param refLoc the location to filter the markers on
 * @param distance the maximum distance of the markers from the refLoc
 * @return void
 */
function _locator_addMarkers(refLoc, distance) {
	var iconSize = _locator_getIconSize(_locator_map.getZoom());
	_locator_icon = new GIcon();
	_locator_icon.iconSize = new GSize(iconSize, 0.5 * iconSize);
	_locator_icon.iconAnchor = new GPoint(iconSize, 5);
	_locator_icon.infoWindowAnchor = new GPoint(iconSize, 0);
	_locator_icon.image = 'images/marker_' + iconSize + '.png';
	_locator_icon.shadow = 'images/shadow_' + iconSize + '.png';
	
	var results = _locator_filterLocations(refLoc, distance);
	
	var locationFilter = results.filter;
	var topN = results.topN;
	
	_locator_markers = new Array(locations.length);
	_locator_messages = new Array(locations.length);
	for (i = 0; i < locations.length; i++) {
		if (locationFilter[i]) {
			var location = locations[i];
			var marker = new GMarker(new GLatLng(location.lat, location.long),
					{
						icon : _locator_icon
					});
			var message = '';
			if((location.name + '') != 'undefined') {
				message += '<strong>' + location.name + '</strong><br />';
			}
			
			if(('' + location.description) != 'undefined') {
				message += location.description;
			}
			_locator_markers[i] = marker;
			_locator_messages[i] = message;
			
			_locator_map.addOverlay(marker);
		}
	}
	
	GEvent.addListener(_locator_map, 'click', function(overlay, latlng, overlaylatlng) {
		if(overlay != null && overlay instanceof GMarker) {
			for(i = 0; i < _locator_markers.length; i++) {
				if(_locator_markers[i] == overlay) {
					_locator_showMarkerMessage(i);
					break;
				}
			}
		}
	});

	if (performFiltering) {
		var listHtml = '<h2>Zoekresultaten</h2>';		
		listHtml += '<table>';
		var numItems = 0;
		for (i = 0; i < topN.length; i++) {
			var item = topN[i];
			if (item.distance < 999) {
				var message = '<span onmouseover="_locator_showMarkerMessage(' + item.index + ');">' + item.location.name + '</span>';
				listHtml += '<tr>';
				listHtml += '<td class="distanceRow">' + _locator_formatNumber(item.distance) + ' km</td>';
				listHtml += '<td class="messageRow">' + message + '</td>';
				listHtml += '</tr>';
				numItems++;
			}
		}
		listHtml += '</table>';
		
		if(numItems == 0) {
			// show text, show contactform
			listHtml = 'Er is in de buurt helaas geen dealer gevonden die de door u geselecteerde Nesling producten op voorraad houdt. Indien u onderstaande gegevens invult nemen wij contact met u op om u toch van dienst te kunnen zijn.';
			listHtml += '<form id="contactform_nohits" onsubmit="return _locator_handleNoHitsForm();"><dl><dt>Naam*</dt><dd><input type="text" id="nohits_naam" name="naam" /></dd>'
						+ '<dt>E-mail*</dt><dd><input type="text" id="nohits_email" name="email" /></dd>'
						+ '<dt>Adres</dt><dd><input type="text" id="nohits_adres" name="adres" /></dd>'
						+ '<dt>Postcode</dt><dd><input type="text" id="nohits_postcode" name="postcode" /></dd>'
						+ '<dt>Plaats</dt><dd><input type="text" id="nohits_plaats" name="plaats" /></dd>'
						+ '<dt>Opmerkingen</dt><dd><textarea id="nohits_opmerkingen" name="opmerkingen"></textarea></dd></dl>'
						+ '<input type="submit" value="verstuur" style="width: 100px; height: 25px;"/></form>';
		}
		$('#list').html(listHtml);
	}
}

/**
 * Calculates the right zoomLevel (it can be overridden), centers the map and calls addMarkers 
 * @param point the point to center on
 * @param zoomLevel the level to zoom on. If -1, it is calculated from the radius
 * @return void
 */
function _locator_geoCoderCallbackFunc(point, zoomLevel) {
	if (point) {
		// calculate zoom level
		var zoom = 7;
		switch (radius) {
		case 50:
			zoom = 9;
			break;
		case 25:
			zoom = 10;
			break;
		case 20:
			zoom = 10;
			break;
		case 15:
			zoom = 10;
			break;
		case 10:
			zoom = 11;
			break;
		case 5:
			zoom = 11;
			break;
		}
		
		if(zoomLevel > -1) {
			zoom = zoomLevel;
		}

		_locator_map.setCenter(point, zoom);
		_locator_addMarkers( {
			lat : point.lat(),
			long : point.lng()
		}, radius);
	}
}

/**
 * Initializes the map
 * @return void
 */
function _locator_initMap() {
	if (GBrowserIsCompatible()) {
		// create map
		_locator_map = new GMap2(document.getElementById("map"));
		_locator_map.setCenter(new GLatLng(52.151917, 5.504150), 7); // show the
		// Netherlands
		_locator_map.setUIToDefault();
		_locator_map.setMapType(G_PHYSICAL_MAP);

		_locator_geocoder = new GClientGeocoder();
		
		if (performFiltering) {

			var address = refLocation;
			address += ' ' + country;

			_locator_geocoder.getLatLng(address, function(point) {
				GEvent.addListener(_locator_map, 'zoomend', function(oldLevel, newLevel) {
					var oldIconSize = _locator_getIconSize(oldLevel);
					var newIconSize = _locator_getIconSize(newLevel);
					if(oldIconSize != newIconSize) {
						_locator_map.clearOverlays();
						_locator_geoCoderCallbackFunc(point, newLevel);
					}
				});
				_locator_geoCoderCallbackFunc(point, -1);
			});
			
			_locator_cacheGeoCoder(0, locationsToCache);

		}
	}
}

/**
 * Recursively loops through the list, adding the values from google to the Cache (in this case: the database) 
 * @param index the index in the list to process
 * @param listToCache the list of locations to cache
 * @return void
 */
function _locator_cacheGeoCoder(index, listToCache) {
	if (index < listToCache.length) {
		var itemToCache = listToCache[index];
		var address = itemToCache.address;
		var id = itemToCache.id;
		
		_locator_geocoder.getLatLng(address, function(point) {
			if(point) {
				$.get('scripts/cacher.php?id=' + id + '&lat=' + point.lat() + '&long='
						+ point.lng());
				_locator_map.addOverlay(new GMarker(point, {icon: _locator_icon}));
			}
			_locator_cacheGeoCoder(index + 1, listToCache);
		});
	}
}

// Returns the icon size for the zoom level
function _locator_getIconSize(zoomLevel) {
	if(zoomLevel > 10)
		return 60;
	
	if(zoomLevel > 8)
		return 40;
	
	return 30;
}

// Formats numbers to exactly one decimal digits and a decimal comma
function _locator_formatNumber(numberToFormat) {
	var stringToFormat = '' + (numberToFormat.toFixed(1));
	return stringToFormat.replace('.', ',');
}

function _locator_showMarkerMessage(markerId) {
	_locator_map.closeInfoWindow();
	if(markerId >= 0) {
		_locator_markers[markerId].openInfoWindowHtml(_locator_messages[markerId]);
		
		var todo = '_locator_addToggleAddressEvent(' + markerId + ');';
		setTimeout(todo, 500);
	}
}

// function to handle extra form
function _locator_handleNoHitsForm() {
	var naam = $('#nohits_naam').val();
	var email = $('#nohits_email').val();
	var adres = $('#nohits_adres').val();
	var postcode = $('#nohits_postcode').val();
	var opmerkingen = $('#nohits_opmerkingen').val();
	
	var regexNaam = /^[a-zA-Z0-9 .]+$/;
	var regexEmail = /^([a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*)@(([a-zA-Z0-9-]+)(\.[a-zA-Z0-9-]+)*(\.[a-zA-Z]{2,4}))$/;
	
	if(regexNaam.test(naam) && regexEmail.test(email)) {
		var postVars = {naam: naam, email: email, adres: adres, postcode: postcode, opmerkingen: opmerkingen};
		
		$('#list').load('scripts/contacthandler.php', postVars);
	} else {
		window.alert('Fout: naam en e-mailadres zijn verplicht.');
	}
	return false;
}

function _locator_addToggleAddressEvent(index) {
	
	$('.addressLink').unbind().bind('click', function() {
		_locator_showAddress(index);
		
		// send e-mail
		var emailerUrl = 'scripts/mailer.php?resellers=' + (locations[index]).id;
		$.get(emailerUrl);
		
		$(this).removeClass('addressLink');
		return false;
	});
}

function _locator_showAddress(index) {
	var marker = _locator_markers[index];
	var message = _locator_messages[index];
	
	message = message.replace('style="display: none"', 'style=""');
	_locator_messages[index] = message;
	
	marker.closeInfoWindow();
	marker.openInfoWindowHtml(message);
}

$(function() {
	$('#country').change(function() {
		if($(this).val() == 'luxemburg') {
			$('#countryMessage').html('Let op: Google Maps kan in Luxemburg alleen overweg met plaatsnamen, niet met postcodes!');
		} else {
			$('#countryMessage').html('');
		}
	});
});

