PowerHour.HappyHour.DIV_GOOGLE_MAP = "divGoogleMap";
PowerHour.HappyHour.DIV_GOOGLE_MAP_CONTAINER = "divGoogleMapContainer";

PowerHour.HappyHour.RouteMode = {
	No	: 0,
	Fastest : 1,
	Personal : 2
};

PowerHour.HappyHour.DEFAULT_OFFSET = 0;

PowerHour.HappyHour.DEFAULT_COUNT = 10;

PowerHour.HappyHour.OnFindReady = null;

/**
 * 
 * @param routeLocations Array<Location>	Liste von Lokalen, die beim Initialisieren zur Karte und zur Route hinzugefuegt werden
 * @param mapId string						ID des HTML-Elements, in dem die Map dargestellt werden soll
 * @param _options object					Einstellungen des Moduls
 * 			@member draggable bool			Gibt an, ob die Routenlokale verschiebbar sein sollen
 * 			@member autoZoomAndCenter bool	Gibt an, ob die Route nach dem Zeichnen automatisch zentriert werden soll
 */
PowerHour.HappyHour.Find = function(routeLocations, mapId, _options) 
{	
	PowerHour.HappyHour.Find.Current = this;
	//Referenz auf die Instanz
	var _this = this;
	//DOM-Elemente mit der Google-Map
	if(!mapId)
		var mapId = PowerHour.HappyHour.DIV_GOOGLE_MAP;
	var mapDiv = document.getElementById(mapId);
	var mapContainer = document.getElementById(PowerHour.HappyHour.DIV_GOOGLE_MAP_CONTAINER);
	//GMap2
	var map = null;
	//GDirections-Objekt
	var dir;
	//GMarker fŸr den Ausgangspunkt des Users
	var userLocationMarker = null;
	//Bankomaten
	var cashmachines = new Array();
	//Google-Search Marker
	var searchresults = new Array();
	//Partyroute (PowerHour.HappyHour.Route)
	this.route = new PowerHour.HappyHour.Route();
	//Modus in dem die Route berechnet werden soll (PowerHour.HappyHour.RouteMode)
	this.routeMode = PowerHour.HappyHour.RouteMode.No;
	//Liste mit happyhours aus dem aktuellen Suchergebnis
	this.happyhours = new Array();
	//Liste mit happyhours der Route
	this.routeLocations = new Array();
	
	// ---------------- EVENT-HANDLER ----------------
	var evOnLocationAdd, evOnShowCashmachines, evOnCalculateRoute, evOnSaveRoute, 
	evOnUserMarkerDragend, evOnRouteLocationClick, evOnRouteDrawEnd;
	
	this.OnLocationAdd = function(callback)
	{
		evOnLocationAdd = callback;
	};
	
	this.OnShowCashmachines = function(callback)
	{
		evOnShowCashmachines = callback;
	};
	
	this.OnCalculateRoute = function(callback)
	{
		evOnCalculateRoute = callback;
	};
	
	this.OnSaveRoute = function(callback)
	{
		evOnSaveRoute = callback;
	};
	
	this.OnUserMarkerDragend = function(callback)
	{
		evOnUserMarkerDragend = callback;
	};
	
	this.OnRouteLocationClick = function(callback)
	{
		evOnRouteLocationClick = callback;
	};
	
	this.OnRouteDrawEnd = function(callback)
	{
		evOnRouteDrawEnd = callback;
	};
	
	function triggerEvent(event, arg0)
	{
		if(typeof(event) == 'function')
			event(arg0);
	}
	// ---------------- END EVENT-HANDLER ----------------
	
	var settings = $.extend(	{
								draggable : true,
								autoZoomAndCenter : true
							}, _options);
	
	if (GBrowserIsCompatible()) 
	{
		map = new GMap2(mapDiv);
		map.setCenter(PowerHour.User.GetGeolocation(), 13);
		map.addControl(new GSmallMapControl());
		map.enableScrollWheelZoom();
		map.enableContinuousZoom();
		//map.setUIToDefault();
    }
	
	if(google.search)
	{
	/****************************
	 * SUCHE
	 ***************************/
	 var searchControl = new google.search.SearchControl();

	  // Initialize a LocalSearch instance
	  searcher = new google.search.LocalSearch(); // create the object
	  searcher.setCenterPoint(map); // bind the searcher to the map
	  
	  var controlContainer = document.createElement('div');
	  var $controlContainer = $(controlContainer);
	  $controlContainer.appendTo('body');
	  $controlContainer.hide();
	  
	  // Create a SearcherOptions object to ensure we can see all results
	  var options = new google.search.SearcherOptions(); // create the object
	  options.setExpandMode(google.search.SearchControl.EXPAND_MODE_OPEN);
	
	  // Add the searcher to the SearchControl
	  searchControl.addSearcher(searcher , options);
	  
	  //And second, we need is a search complete callback!
	  searchControl.setSearchCompleteCallback(searcher , function() {
		  var results = searcher.results; // Grab the results array
		  // We loop through to get the points
		  for (var i = 0; i < results.length; i++) 
		  {
			  	var result = results[i]; // Get the specific result
		  		var markerLatLng = new google.maps.LatLng(parseFloat(result.lat),
	                                            parseFloat(result.lng));
		  			var marker = new google.maps.Marker(markerLatLng, 
		  												PowerHour.GoogleMap.Icons.GetFoodIcon()); // Create the marker
	
			  // Bind information for the infoWindow aka the map marker popup
			  marker.bindInfoWindow(result.html.cloneNode(true));
			  result.marker = marker; // bind the marker to the result
			  map.addOverlay(marker); // add the marker to the map
			  searchresults.push(marker);
			}
			
	  });
	  
	  searchControl.draw(controlContainer);
	}
	  
	if(settings.draggable)
	{
		//"Warenkorb" fŸr Route
		$("#olRouteLocations").sortable({
												update : function()
												{
													//Neu berechnen
													PowerHour.HappyHour.Find.Current.routeMode = PowerHour.HappyHour.RouteMode.Personal;
													PowerHour.HappyHour.Find.Current.calculateRoute();
												}
		});
		
		$("#olRouteLocations").disableSelection();
	}
	
	$('.liRouteLocation>a').live('click',function()
	{
		var id = parseInt($(this).parent().attr('id').replace(/[^0-9]/g,''));
		var location = PowerHour.HappyHour.Find.Current.getLocationById(id);
		GEvent.trigger(location.marker,'click');
		
		triggerEvent(evOnRouteLocationClick, location);
	});
	
	//Location aus der Route entfernen
	$('a.removeRouteLocation').live('click',function()
	{
		var id = parseInt($(this).parent().attr('id').replace(/[^0-9]/g,''));
		var location = PowerHour.HappyHour.Find.Current.getLocationById(id);

		PowerHour.HappyHour.Find.Current.route.removeRouteLocation(location);
		
		if(PowerHour.HappyHour.Find.Current.route.getHappyhoursCount() <= 0)
		{
			if($('#divPartyrouteContainer').length > 0)
			{
				$('#divPartyrouteContainer').hide();
			}
			_this.clearPolyline();
		}
		else
		{
			if($(mapDiv).is(':visible'))
				PowerHour.HappyHour.Find.Current.calculateRoute();
		}
		
		return false;
	});
	
	/*$('#linkChangePosition').live('click', function()
	{
		PowerHour.User.ShowChangeGeolocationWindow(function()
		{
			with(PowerHour.HappyHour.Find.Current)
			{
				route.changeStartpoint(PowerHour.User.GetGeolocation());
				calculateRoute();
			}
		});
		
		return false;
	});*/
	
	/**
	 * Instanz der GMap
	 * @return
	 */
	this.getMap = function()
	{
		return map;
	};
	
	/**
	 * Blendet Marker ein, die auf den aktuellen Suchbegriff zutreffen
	 * @param search	Suchbegriff
	 */
	this.showSearchResults = function(search)
	{
		searchControl.execute(search);
	};
	
	this.clearSearchResults = function()
	{
		while(searchresults.length > 0)
		{
			map.removeOverlay(searchresults.pop());
		}
	};
	
	/**
	 * Gibt die aktuelle Sortierung der Lokale zurueck
	 * @return String Sortiermodus (z.B. begin, begin_desc, ...)
	 */
	this.getSortMode = function()
	{
		var $selected = $('a.findSort.active');
		
		var mode = $selected.attr('id').replace(/sort/,'').toLowerCase();
		
		if($selected.hasClass('desc'))
		{
			mode += '_desc';
		}
		
		return mode;
	};
	
	/**
	 * Laedt die gespeicherten User-Routen und gibt diese aus
	 */
	this.listHappyhourRoutes = function()
	{
		//TODO Routen-Liste stylen
		$.get("/userroute/list",function(res)
		{
			$('#divListHappyhourRoutes').html(res);
			if(!$('#divListHappyhourRoutes').is(':visible'))
			{
				$('#divListHappyhourRoutes').slideDown();
			}
		});
	};
	
	this.clearPolyline = function()
	{
		if(dir)
		{
			if(dir.getPolyline())
			{
				map.removeOverlay(dir.getPolyline());
			}
		}
	};
	
	this.addHappyHours = function(happyhours, addToRoute)
	{
		var i = 0;
		for(i = 0; i < happyhours.length; i++)
		{
			_this.addHappyHour(happyhours[i], addToRoute);
		}
	};
	
	this.clearHappyHours = function()
	{
		var i;
		for(i = 0; i < _this.happyhours.length; i++)
		{
			//Marker von der Map loeschen
			map.removeOverlay(_this.happyhours[i].marker);
		}
		delete _this.happyhours;
		_this.happyhours = new Array();
		
		//Route zuruecksetzen
		_this.route.clearHappyhours();
	};
	
	/**
	 * FŸgt eine HappyHour-Location in der Map hinzu
	 * @param location Object	Name der Location
	 * @param addToRoute bool	Gibt an, ob die Location auch zur Route hinzugefŸgt werden soll
	 * @param options Object	Objekt mit zusŠtzlichen Optionen (z.B. icon)
	 */
	this.addHappyHour = function(happyhour, addToRoute, options)
	{
		if(!_this.happyhours.hasValue(happyhour, 'locationId'))
		{
			// Default-Options erweitern
			var defaults = {
				icon : PowerHour.GoogleMap.Icons.GetLocationIcon()
			};
			var options = $.extend(defaults, options);
			var point = new GLatLng(happyhour.locationPosX, happyhour.locationPosY);
			var marker = new GMarker(point, options.icon);
			marker.bindInfoWindowHtml(happyhour.htmlForGoogleMarker);
			map.addOverlay(marker);
			
			happyhour.marker = marker;
			
			_this.happyhours.push(happyhour);
			
			if(addToRoute)
			{
				_this.route.addRouteLocation(happyhour);
			}
			
			if(_this.routeMode != PowerHour.HappyHour.RouteMode)
			{
				
			}
			//Wenn es das erste Lokal ist
			if(_this.happyhours.length == 1)
				map.setCenter(marker.getLatLng()); //Karte auf dieses zentrieren
			triggerEvent(evOnLocationAdd);
		}
	};
	
	if(routeLocations && routeLocations.length)
	{
		_this.addHappyHours(routeLocations, true);
		//this.routeLocations = routeLocations;
		//this.route.addRouteLocations(routeLocations);
	}
	
	/**
	 * Berechnet die schnellste Route anhand der gegebenen Suchergebnisse
	 * und zeichnet diese in der Map ein (ausgehend von der ClientLocation)
	 * 
	 * @param printRoute bool	Gibt an, ob die Route auch gedruckt werden soll
	 * @todo Ladeanzeige wŠhrend dem Berechnen der Route
	 */
	this.calculateRoute = function(printRoute)
	{
		var rPoints;
		
		_this.route.getStartpointAddress();
		
		if(this.routeMode == PowerHour.HappyHour.RouteMode.Fastest)
		{
			rPoints = _this.route.calculateBest();
		}
		else if(_this.routeMode == PowerHour.HappyHour.RouteMode.Personal)
		{
			rPoints = _this.route.calculatePersonal();
		}
		
		delete _this.routeLocations;
		_this.routeLocations = _this.route.getRouteLocations();
		
		triggerEvent(evOnCalculateRoute);
		
		drawRoute(rPoints, (this.routeMode == PowerHour.HappyHour.RouteMode.Fastest), printRoute);
	};
	
	/**
	 * Speichert die Route fŸr den User
	 * @param routeId	ID der zu speichernden Route
	 */
	this.saveRoute = function(routeId)
	{
		//Nur fuer eingeloggte User moeglich
		if(PowerHour.User.IsLoggedIn)
		{
			if(_this.route)
			{
				if(_this.route.getHappyhoursCount() > 0 && _this.route.getRouteLocationsCount() == 0)
				{
					_this.routeMode = PowerHour.HappyHour.RouteMode.Fastest;
					_this.calculateRoute();
				}
				if(routeId > 0)
				{
					_this.route.routeId = routeId;
				}
				_this.route.saveRoute();
				triggerEvent(evOnSaveRoute);
			}
			else
				PowerHour.Alert("Sorry, es liegt keine Route zum Speichern vor!", "Fehler");
		}
		else
		{
			PowerHour.Alert("Sorry, f&uuml;r diese Funktion musst du eingeloggt sein!", "Fehler");
		}
	};
	
	/*
	 * Route drucken
	 */
	$('#linkPrintRoute').click(function()
	{
		with(PowerHour.HappyHour)
		{
			if(Find.Current.routeMode == RouteMode.No)
				Find.Current.routeMode = RouteMode.Fastest;
			Find.Current.calculateRoute();
			
			if($(this).tagName == 'a')
			{
				$(this).attr('target','_blank');
				$(this).attr('href',Find.Current.route.getPrintUrl());
			}
			else
			{
				window.open(Find.Current.route.getPrintUrl());
			}
			
			return (Find.Current.route.getRouteLocationsCount() > 0);
		}
	});
	
	/**
	 * Laedt eine bestimmte Anzahl an Suchergebnissen fuer die aktuelle Abfrage
	 * @param from	Offset-Index von dem aus geladen werden soll
	 * @param count	Anzahl der zu ladenden Lokale
	 */
	this.loadLocationsAjax = function(from, count, options, callback)
	{
		var order = this.getSortMode();
		
		if(!from)from = PowerHour.HappyHour.DEFAULT_OFFSET;
		if(!count)count = PowerHour.HappyHour.DEFAULT_COUNT;
		
		PowerHour.Ajax.FreezeAndLoad(	'#divResultsContainer', 
										'/happyhours/findajax/sort/' + order + 
										'/offset/' + from + 
										'/count/' + count + 
										window.location.search, options, function(res)
										{
											if(typeof(callback) == 'function')
												callback();
										});
	};
	
	/**
	 * Sucht nach der Location mit der angegebenen ID
	 * @param int id	ID der Location
	 * 
	 * @return location
	 */
	this.getLocationById = function(id)
	{
		var location;
		for(i = 0; i < _this.happyhours.length; i++)
		{
			location = _this.happyhours[i];
			if(location.locationId == id)
			{
				return location;
			}
		}
		return null;
	};
	
	/**
	 * Gibt eine Alert-Message aus, wenn keine Lokale in der Route vorhanden sind
	 */
	this.noRouteLocationsFound = function()
	{
		PowerHour.Alert("Du hast noch keine Locations zu deiner Route hinzugef&uuml;gt!","Fehler!", true);
	};
	
	this.clearCashmachines = function()
	{
		while(cashmachines.length > 0)
		{
			map.removeOverlay(cashmachines.pop());
		}
	};
	
	this.showCashmachines = function()
	{
		showCashmachines(map.getCenter());
	};
	
	/**
	 * Sucht nach Bankomaten in der NŠhe von searchPoint und blendet diese in der Map ein
	 * @param searchPoint GLatLng	Punkt fŸr den Bankomaten gesucht werden soll
	 * 
	 * @todo Vorherige Marker fŸr Bankomaten ausblenden, Methode aufrufen
	 */
	function showCashmachines(searchPoint)
	{
		if(!searchPoint)
		{
			searchPoint = map.getCenter();
		}
		PowerHour.Cashmachines.GetCashmachinesNear(	searchPoint.lat(), searchPoint.lng(), 
			function(cm)
			{
				_this.clearCashmachines();
				for(i = 0; i < cm.length; i++)
				{
					var marker = new GMarker(new GLatLng(cm[i].positionX, cm[i].positionY), PowerHour.GoogleMap.Icons.GetCashmachineIcon());
					marker.bindInfoWindowHtml(cm[i].htmlForGoogleMarker);
					map.addOverlay(marker);
					cashmachines.push(marker);
				}
				triggerEvent(evOnShowCashmachines);
			});
	};
	
	/**
	 * Ersetzt das Marker-Icon der angegebenen Location durch das mitgegebene GIcon
	 * @param location Object|int	Instanz oder ID der Location
	 * @param gicon GIcon			GIcon das absofort gezeigt werden soll
	 */
	this.changeMarkerIcon = function(location, gicon)
	{
		if(typeof(location) != 'object')
		{
			location = this.getLocationById(location);
		}
		
		if(!gicon)
		{
			gicon = PowerHour.GoogleMap.Icons.GetLocationIcon();
		}
		
		//Marker entfernen
		map.removeOverlay(location.marker);
		//Neuen Marker aus bisherigen Daten erstellen
		location.marker = new GMarker(
				new GLatLng(	location.locationPosX,
								location.locationPosY),
								gicon); //Neues GIcon
		//HTML-Info-Text fŸr den Marker wieder festlegen
		location.marker.bindInfoWindowHtml(location.htmlForGoogleMarker);
		//Marker in der Map zeigen
		map.addOverlay(location.marker);
	};
	
	this.resetZoomAndCenterToRoute = function()
	{
		if(map && dir)
		{
			//GBounds der Route laden
			var bounds = dir.getBounds();
			var zoom = map.getBoundsZoomLevel(bounds);
			//Zoom festelegen
			map.setZoom(zoom);
			//Karte zentrieren
			map.setCenter(bounds.getCenter());
		}
	};
	
	/**
	 * Zeichnet eine Route anhand von LatLngs und sortiert die Suchergebnisse ggf. neu
	 * @param Array points		Array mit LatLng Ÿber die die Route fŸhren soll
	 * @param bool reorder		Gibt an, ob die Suchergebnisse neu sortiert werden mŸssen
	 * @param bool printRoute	Gibt an, ob die Route nach der Berechnung gedruckt werden soll
	 */
	function drawRoute(points, reorder, printRoute)
	{
		if(	userLocationMarker && 
			!userLocationMarker.getLatLng().equals(PowerHour.User.GetGeolocation()))
		{
			map.removeOverlay(userLocationMarker);
			userLocationMarker = PowerHour.User.GetGeolocationMarker(evOnUserMarkerDragend);
			map.addOverlay(userLocationMarker);
		}
		else if(!userLocationMarker)
		{
			userLocationMarker = PowerHour.User.GetGeolocationMarker(evOnUserMarkerDragend);
			map.addOverlay(userLocationMarker);
		}
		
		if(dir)
		{
			if(dir.getPolyline())
			{
				map.removeOverlay(dir.getPolyline());
			}
		}
		
		dir = new GDirections(	null,
								document.getElementById('divGoogleMapRouteHidden'));
		
		$('#divGoogleMapRouteHidden').html('');
		
		if(points && points.length <= 1)
		{
			_this.noRouteLocationsFound();
			return;
		}
		
		GEvent.addListener(dir, 'load',	function()
										{
											map.addOverlay(dir.getPolyline());
											//GBounds der Route laden
											var bounds = dir.getBounds();
											var zoom = map.getBoundsZoomLevel(bounds);
											if(settings.autoZoomAndCenter)
											{
												//Zoom festelegen
												map.setZoom(zoom);
												//Karte zentrieren
												map.setCenter(bounds.getCenter());
											}
											
											if(reorder)
											{
												var previousResult = null;
												var recentResult = null;
												for(h = 0; h < _this.routeLocations.length; h++)
												{
													recentResult = $('#liRouteLocation' + _this.routeLocations[h].locationId);
													if(!previousResult)
													{
														if($('.liRouteLocation').eq(0).attr('id') != recentResult.attr('id'))
															$('.liRouteLocation').eq(0).before(recentResult);
													}
													else
													{
														try
														{
															previousResult.after(recentResult);
														}
														catch(e){}
													}
													previousResult = recentResult;
												}
											}
											if($("#olRouteLocations").length > 0)
												$("#olRouteLocations").sortable('refresh');
											
											for(r = 0; r < _this.routeLocations.length; r++)
											{
												//Marker tauschen
												_this.changeMarkerIcon(_this.routeLocations[r], PowerHour.GoogleMap.Icons.GetCocktailIcon());
											}
											
											triggerEvent(evOnRouteDrawEnd);
											
											if(printRoute)
												_this.route.print();
										});
		
		dir.loadFromWaypoints(points, { 
										getPolyline : true, 
										travelMode : G_TRAVEL_MODE_WALKING,
										getSteps : true
										});
		
	};
	
	if(typeof(PowerHour.HappyHour.OnFindReady) == 'function') PowerHour.HappyHour.OnFindReady();
	
};
/**
 * Instanz der aktuellen Suche
 */
PowerHour.HappyHour.Find.Current = null;
/**
 * Erstellt einen neue Instanz der aktuellen Suche
 */
PowerHour.HappyHour.Find.Init = function()
{
	new PowerHour.HappyHour.Find();
};

/**
 * Klasse fuer das berechnen der Partyroute
 */
PowerHour.HappyHour.Route = function(startpoint)
{
	var _this = this;
	var routePoints = new Array();
	var routeLocations = new Array();
	var happyhours = new Array();
	
	//EVENT HANDLER
	var evOnChangeStartPoint, evOnLocationRemove, evOnLocationAdd;
	
	this.OnChangeStartPoint = function(callback)
	{
		evOnChangeStartPoint = callback;
	};
	
	this.OnLocationRemove = function(callback)
	{
		evOnLocationRemove = callback;
	};
	
	this.OnLocationAdd = function(callback)
	{
		evOnLocationAdd = callback;
	};
	
	this.routeId = null;
	
	if(!startpoint)
	{
		var startpoint = PowerHour.User.GetGeolocation();
	}
		
	/**
	 * Setzt die Variablen routeLocations und routePoints zurŸck und gibt deren Speicher frei
	 */
	function reset()
	{
		delete routePoints;
		delete routeLocations;
		routePoints = new Array();
		routeLocations = new Array();
	}
	
	/**
	 * Berechnet ausgehend vom Startpoint die schnellste Route
	 * @return Array	Schnellste Route
	 */
	this.calculateBest = function()
	{		
		var tmp = happyhours.clone();
		var tmpStartpoint = startpoint;
		
		reset();
		
		routePoints.push(tmpStartpoint);
		while(happyhours.length > 0)
		{
			tmpStartpoint = getNearestPoint(tmpStartpoint);
			routePoints.push(tmpStartpoint);
		}
		
		happyhours = tmp;
		
		return routePoints;
	};
	
	/**
	 * Berechnet ausgehend vom Startpoint die Route in der vom User gewaehlten Reihenfolge
	 * @return Array	berechnete Route
	 */
	this.calculatePersonal = function()
	{
		reset();
		
		if($('#olRouteLocations').length > 0)
		{
			var order = $('#olRouteLocations').sortable('toArray');
			_this.clearHappyhours();
			//Neu berechnen
			var l, id, location;
			for(l = 0; l < order.length; l++)
			{
				id = parseInt(order[l].replace(/[^0-9]/g,''));
				location = PowerHour.HappyHour.Find.Current.getLocationById(id);
				_this.addRouteLocation(location);
			}
		}
		
		routePoints.push(startpoint);
		var i;
		for(i = 0; i < happyhours.length; i++)
		{
			routePoints.push(new GLatLng(
										happyhours[i].locationPosX, 
										happyhours[i].locationPosY));
		}
		
		routeLocations = happyhours.clone();
		
		return routePoints;
	};
	
	/**
	 * Fuegt eine Location, sofern diese noch nicht hinzugefuegt wurde, der Route hinzu
	 */
	this.addRouteLocation = function(location)
	{
		if(location && !happyhours.hasValue(location, 'locationId'))
		{
			happyhours.push(location);
			if($('#olRouteLocations').length > 0)
			{
				var li = $('#liRouteLocationTemplate').clone();
				li.attr('id','liRouteLocation' + location.locationId);
				li.removeClass('hidden');
				li.find('span.title').text(location.locationName);
				$('#olRouteLocations').append(li);
				$("#olRouteLocations").sortable('refresh');
			}
			if(typeof(evOnLocationAdd) == 'function') evOnLocationAdd(location);
			/*
			 * @TODO nur highlighten wenn neu hinzugefügt, nicht nach sortieren zb
			 */
			PowerHour.Highlight($('#olRouteLocations li#liRouteLocation' + location.locationId + ' a'));
		}
	};
	
	/**
	 * Fuegt ein Array von Locations zur Route hinzu
	 */
	this.addRouteLocations = function(locations)
	{
		var l = 0;
		for(l = 0; l < locations.length; l++)
		{
			_this.addRouteLocation(locations[l]);
		}
	};
	
	/**
	 * Entfernt eine Location aus der Route
	 */
	this.removeRouteLocation = function(location)
	{
		//var lid = location.locationId;
		if(happyhours.remove(location, 'locationId'))
		{
			$('#liRouteLocation' + location.locationId).remove();
			//LocationMarker zurŸcksetzen
			PowerHour.HappyHour.Find.Current.changeMarkerIcon(location);
			if(typeof(evOnLocationRemove) == 'function') evOnLocationRemove(location);
		}
	};
	
	/**
	 * Entfernt alle Happyhours aus der Route
	 */
	this.clearHappyhours = function()
	{
		delete happyhours;
		happyhours = new Array();
		if($('.liRouteLocation').length > 0)
			$('.liRouteLocation:visible').remove();
	};
	
	/**
	 * Gibt die Locations entlang der Route in der richtigen Reihenfolge zurŸck
	 * @return Array
	 */
	this.getRouteLocations = function()
	{
		return routeLocations;
	};
	
	/**
	 * @return int Anzahl der Route-Locations
	 */
	this.getRouteLocationsCount = function()
	{
		if(routeLocations)
			return routeLocations.length;
		return 0;
	};
	
	this.getHappyhours = function()
	{
		return happyhours;
	};
	
	
	this.getHappyhoursCount = function()
	{
		if(happyhours)
			return happyhours.length;
		return 0;
	};
	
	/**
	 * …ffnet ein Popup mit der druckbaren Route
	 */
	this.getPrintUrl = function()
	{		
		return PowerHour.GoogleMap.getPrintUrl(getRouteLocationsAddresses(true));
	};
	
	/**
	 * Aendert den Startpoint von dem aus die Route berechnet wird
	 * @param point GLatLng	Startpunkt
	 */
	this.changeStartpoint = function(point)
	{
		startpoint = point;
		if(typeof(evOnChangeStartPoint) == 'function')
		{
			evOnChangeStartPoint();
		}
	};
	
	/**
	 * Rechnet den Startpoint (GLatLng) in eine Adresse um und uebergibt das Ergebnis an eine Callback-Funktion
	 * @param callback function	Callback-Funktion (erhaelt die Adresse als String-Parameter)
	 */
	this.getStartpointAddress = function(callback)
	{
		var geocoder = new GClientGeocoder();
		geocoder.getLocations(startpoint, function(response)
		{
			if(response.Status.code == 200)
			{
				$('#divUserAddress').html(response.Placemark[0].address);
				$('#divUserAddressContainer').removeClass('hidden');
				if(typeof(callback) == 'function')
				{
					callback(response.Placemark[0].address);
				}
			}
		});
	};
	
	/**
	 * @return Array mit den IDs der Routen-Locations
	 */
	function getRouteLocationsIds()
	{
		var ids = new Array();
		var i = 0;
		for(i = 0; i < routeLocations.length; i++)
		{
			ids.push(routeLocations[i].locationId);
		}
		
		return ids;
	}
	
	function getRouteLocationsAddresses(inklStartpoint)
	{
		var addresses = new Array();
		
		if(inklStartpoint)
		{
			addresses.push(startpoint.toUrlValue());
		}
		
		var i = 0;
		for(i = 0; i < routeLocations.length; i++)
		{
			addresses.push(	routeLocations[i].locationAddress + '(' + routeLocations[i].locationName + ')');
		}
		
		return addresses;
	}
	
	/**
	 * Speichert die aktuelle Route (je nach RouteId wird eine neue angelegt bzw. eine vorhandene bearbeitet)
	 */
	this.saveRoute = function()
	{
		if(!this.routeId) //Neu anlegen
		{
			PowerHour.Prompt(
								"Bitte lege einen Titel f&uuml;r deine Route fest:", 
								'Route speichern', 
								[
								 {
									 name : 'title',
									 type : 'text',
									 label : 'Titel:'
								 },
								 {
									 name : 'weekday',
									 type : 'select',
									 label : 'Wochentag:',
									 items : [
									          	{ value : 1, text : 'Montag'},
									          	{ value : 2, text : 'Dienstag'},
									          	{ value : 3, text : 'Mittwoch'},
									          	{ value : 4, text : 'Donnerstag'},
									          	{ value : 5, text : 'Freitag'},
									          	{ value : 6, text : 'Samstag'},
									          	{ value : 7, text : 'Sonntag'}
									          ]
								 }
								], 
								function(res)
								{
									if(res)
									{
										saveRoute(res.title, res.weekday);
									}
								});
		}
		else //bearbeiten
		{
			saveRoute();
		}
	};
	
	/**
	 * Speichert die aktuelle Route unter dem angegebenen Titel
	 * @param title String	Titel der Route
	 * @param weekday int	Wochentag (1: Montag, 7: Sonntag)
	 */
	function saveRoute(title, weekday)
	{
		var href = '/userroute/add';
		if(_this.routeId)
		{
			href += '/routeId/' + _this.routeId;
		}
		
		$.post(	href,
				{
					'locations' : getRouteLocationsIds(),
					'name' : title,
					'weekDayNum' : weekday
				},
				function(res)
				{
					if(res)
					{
						PowerHour.Alert(
										'Deine Route kannst du <a href="/userroute/edit/routeId/'+res.id+'">hier' + 
										'</a> erneut aufrufen.',
										'Route gespeichert');
										
					}
				});
	}
	
	/**
	 * Gibt den Index des am nahe gelegensten LatLng zurŸck
	 * @param int latlng	LatLng mit dem verglichen werden soll
	 * 
	 * @return int 			Index des nŠchsten LatLng
	 */
	function getNearestPoint(latlng)
	{
		var nearest = 0;
		var point = latlng;
		var point2;
		var nearestPoint = new GLatLng(happyhours[0].locationPosX, happyhours[0].locationPosY);
		var j;
		
		for(j = 0; j < happyhours.length; j++)
		{
			point2 = new GLatLng(happyhours[j].locationPosX, happyhours[j].locationPosY);
			if(	point.distanceFrom(point2) < point.distanceFrom(nearestPoint))
			{
				nearest = j;
			}
			nearestPoint = new GLatLng(happyhours[nearest].locationPosX, happyhours[nearest].locationPosY);
		}
		
		routeLocations.push(happyhours[nearest]);
		happyhours.splice(nearest,1);
		
		return nearestPoint;
	};
	
};
