var LVB_GM = (function(){
	return {
		init: function()
		{
			var that = this;

			// =================
			// = CONFIGURATION =
			// =================
			
			this.default_map_center_lat = 51.3405575;
			this.default_map_center_lng = 12.3742745;
			this.default_map_zoom       = 13;
			this.minimum_input_length   = 1;
			
			// TODO: error messages i18n
			this.error_messages = {
				generic: {
					de: "Es ist ein Fehler aufgetreten.",
					en: "An error occured.",
					fr: "An error occured."
				},
				invalid: {
					de: "Bitte füllen Sie dieses Feld aus.",
					en: "Invalid input.",
					fr: "Invalid input."
				}
			};
			
			this.i18n = {
				map_type_day: {
					de: "Tag",
					en: "Daytime",
					fr: "Jour"
				},
				map_type_night: {
					de: "Nacht",
					en: "Night-time",
					fr: "Nuit"
				}
			};
			
			// =====================
			// = END CONFIGURATION =
			// =====================
			
			// ==============
			// = INIT STUFF =
			// ==============
			
			this.connections    = [];
			this.lvb_pois       = [];
			this.poi_categories = {};
			this.stacked_reports = [];
			this.trigger_search = false;
			
			// ====================
			// = INIT GOOGLE MAPS =
			// ====================
			
			$('#dialog').dialog({autoOpen: false});
			
			if (!GBrowserIsCompatible())
			{
				this.is_js_compatible(true);
				return;
			}
			
			$("#from").focus();
			
			this.map = this.setup_google_maps(
				"map",
				{mapTypes: [G_NORMAL_MAP, G_SATELLITE_MAP, G_HYBRID_MAP]});

			this.map.setCenter(
				new google.maps.LatLng(this.default_map_center_lat, this.default_map_center_lng),
				this.default_map_zoom);
			this.map.setMapType(G_NORMAL_MAP);
			
			this.icon                  = new google.maps.Icon();
			this.icon.image            = this.asset_url+"/images/hst.png";
			this.icon.iconSize         = new google.maps.Size(24,24);
			this.icon.iconAnchor       = new google.maps.Point(4,4);
			this.icon.infoWindowAnchor = new google.maps.Point(12,12);

			this.icon_start                  = new google.maps.Icon();
			this.icon_start.image            = this.asset_url+"/images/map_icon_start.png";
			this.icon_start.iconSize         = new google.maps.Size(19,22);
			this.icon_start.iconAnchor       = new google.maps.Point(9,22);
			this.icon_start.infoWindowAnchor = new google.maps.Point(16,18);

			this.icon_via                  = new google.maps.Icon();
			this.icon_via.image            = this.asset_url+"/images/map_icon_via.png";
			this.icon_via.iconSize         = new google.maps.Size(19,22);
			this.icon_via.iconAnchor       = new google.maps.Point(16,22);
			this.icon_via.infoWindowAnchor = new google.maps.Point(16,18);

			this.icon_ziel                  = new google.maps.Icon();
			this.icon_ziel.image            = this.asset_url+"/images/map_icon_ziel.png";
			this.icon_ziel.iconSize         = new google.maps.Size(19,22);
			this.icon_ziel.iconAnchor       = new google.maps.Point(16,22);
			this.icon_ziel.infoWindowAnchor = new google.maps.Point(16,18);

			this.icon_umsteigen                  = new google.maps.Icon();
			this.icon_umsteigen.image            = this.asset_url+"/images/umsteigen.png";
			this.icon_umsteigen.iconSize         = new google.maps.Size(24,24);
			this.icon_umsteigen.iconAnchor       = new google.maps.Point(12,24);
			this.icon_umsteigen.infoWindowAnchor = new google.maps.Point(12,12);

			this.report_icon                  = new google.maps.Icon();
			this.report_icon.image            = this.asset_url+"/images/report.png";
			this.report_icon.iconSize         = new google.maps.Size(28,26);
			this.report_icon.iconAnchor       = new google.maps.Point(14,13);
			this.report_icon.infoWindowAnchor = new google.maps.Point(14,15);
			
			
			// ================
			// = Load reports =
			// ================
			
			// this.lvb_reports
			$.getJSON(
				this.ajax_url + "?mode=reports",
				function(data) {
					that.lvb_reports = data;
					that.show_report();
					$.each(that.stacked_reports, function(index, item) {
						that.show_report(item, true);
					})
				}
			);
			
			// ================================
			// = BIND INTERFACE FUNCTIONALITY =
			// ================================
						
			$("#xhr_spinner").bind("ajaxSend", function(){
				$(this).show();
			}).bind("ajaxComplete", function(){
				$(this).hide();
			});
			
			$("#form_timetable").find("select.timetable_select").bind("change", function(){
				var current_select = $(this);
				if(current_select.val() != "NULL" && current_select.length > 0)
				{
					var selects = $("#form_timetable").find("select.timetable_select:not(#" + current_select.attr('id') + ")");
					selects.each(function(){
						$(this).find("option:eq(0)").attr("selected","selected");
					})
					that.find_timetables(this);
				}
				return false;
			});			
			
			$("#timetable_date").bind("change", function(){
				$(".timetable_changes_select option:eq(0)").attr("selected", "selected");
				that.find_timetables(this);
				return false;
			});
			
			$(".timetable_changes_select").bind("change", function(){
				that.find_timetables(this);
				return false;
			});
			
			$("#timetable_directions").bind("change", function(){
				that.toggle_timetables(this);
				return false;
			});

			$("#form_connection").bind("submit", function(){
				that.prepare_find_connections();
				return false;
			});

			$("#form_stop").bind("submit", function(){
				that.find_schedule(JSON.stringify($(this).serializeArray()));
				return false;
			});
			
			$(".show_specific_line").live("click", function(){
				that.show_specific_line($(this).text(), '');
			});
			
			$("#action_earlier").live("click", function () {that.schedule_more(-1); return false;});
			$("#action_later").live("click", function () {that.schedule_more(1); return false;});
			
			$("#connection_add_to_mylvb").live("click", function(){
				this.disabled = true;
				that.add_connection_to_mylvb();
			});
			
			$("#monitor_add_to_mylvb").live("click", function(){
				this.disabled = true;
				that.add_monitor_to_mylvb();
			});

			$.timeEntry.regional['de'] = {
				show24Hours: true,
				separator: ':',
				ampmPrefix: '',
				ampmNames: ['', ''],
				spinnerTexts: ['Jetzt', 'vorheriges Feld', 'nächstes Feld', 'hoch', 'runter'],
				useMouseWheel: false
			};

			$.timeEntry.setDefaults($.timeEntry.regional['de']);
			$("input.time").timeEntry({spinnerImage: false});

			$.datepicker.setDefaults($.datepicker.regional['de']);
			$("input.date").datepicker();
			
			// Tabs an/aus
			$(".lvb_maps_show .sideNavigation .tab_trigger").bind("click", function(){
																									
				$(".tab .tab_content").addClass('initially_hidden');
				$(".sideNavigation .tab_trigger").removeClass('on');
				$(this).addClass('on');
				
				var el = $(this).find('a');
				$($(el).attr('href')).find(".tab_content").toggleClass('initially_hidden');
				
				
				if(el.parent().hasClass("connection") && $("#routing_container").children().length)
					$("#routing_container").removeClass('initially_hidden');
				else
					$("#routing_container").addClass('initially_hidden');
				
				if (el.parent().hasClass('net')) {
					$('#mapscontent').css('height', $("#map").height() + 30);
					$("#map").hide();
				} else {
					$("#map").show();
					$('#mapscontent').css('height', 'auto');
				}
				
				if (el.parent().hasClass("tab_trigger_poi") && !this.poi_loaded)
				{
					$.ajax({
						url: that.ajax_url,
						data: {mode: "poi"},
						success: function(html){
							$("#tab_contents_poi").html(html);
						},
						error: function(XMLHttpRequest, textStatus, errorThrown){
							// console.log("Error: " + errorThrown + " :: " + textStatus);
						}
					});
					this.poi_loaded = true;
				}
				
				if ($(this).hasClass('tab_lnp'))
				{
				  that.show_line_net_plan();
				}
				
				return false;
				
			});
			
			$(".tab_content").each(function(index, item){
				var mode = that.mode ? that.mode : "connection";
				if (!$(item).parent().hasClass(mode))
				{
					$(item).addClass("initially_hidden");
				}
			});
			
			$.each(this.commands, function(index, command) {
				var function_name = command.command;
				if (typeof that[function_name] == 'function')
				{
					that[function_name].apply(that, command.args || []);
				}
			});
			
			this.fix_select_width();
			
			this.show_report();
		},
		
		is_js_compatible: function(force_off)
		{
			if (force_off)
			{
				$("div.sidebar_maps, #map, p.insa").addClass("hideme");
				$("#no_js_warning").removeClass("hideme");
			}
			else
			{
				$("div.sidebar_maps, #map, p.insa").removeClass("hideme");
				$("#no_js_warning").addClass("hideme");
			}
		},
		
		setup_google_maps: function(element_id, options, hideMapTypes)
		{
			var map = new google.maps.Map2(document.getElementById(element_id), options);
			if (!hideMapTypes)
				map.addControl(new google.maps.MapTypeControl(true)); // Standard-Controller der Kartentypen
			map.addControl(new google.maps.LargeMapControl());    // erweiterte Zoom-Anzeige
			return map;
		},
		
		toggle_poi_category: function(id)
		{
			if (!this.lvb_pois)
			{
				return;
			}
			
			var force_mode = 1;
			if (this.poi_categories[id])
			{
				this.poi_categories[id] = false;
				force_mode = -1;
			}
			else
			{
				this.poi_categories[id] = true;
			}

			var that = this;
			$.each(this.lvb_pois, function (index, poi) {
				if (poi.category_id == id)
				{
					that.toggle_poi(index, force_mode, true);
				}
			});
			
		},
		
		// force_mode = 0 standard toggle, 1 force on, -1 force off
		toggle_poi: function(id, force_mode, should_not_pan_to_point)
		{
			if (!this.lvb_pois || !this.lvb_pois[id])
			{
				return;
			}

			var poi = this.lvb_pois[id];
			if (poi.marker)
			{
				if (force_mode == -1)
				{
					this.map.removeOverlay(poi.marker);
					poi.marker = null;
				}
				else
				{
					poi.marker.openInfoWindowHtml($("#gm_info_poi_"+id).html());
				}
			}
			else
			{
				if (force_mode != -1)
				{
					var icon              = new google.maps.Icon();
					icon.image            = poi.icon.url;
					icon.iconSize         = new google.maps.Size(poi.icon.width, poi.icon.height);
					icon.iconAnchor       = new google.maps.Point(poi.icon.anchor_x, poi.icon.anchor_y);
					icon.infoWindowAnchor = new google.maps.Point(poi.icon.info_anchor_x, poi.icon.info_anchor_y);

					var point = new google.maps.LatLng(poi.lat, poi.lng);
					var marker = new google.maps.Marker(point, icon);

					marker.poi_id = id;
					GEvent.addListener(marker, "click", function() {
						this.openInfoWindowHtml($("#gm_info_poi_"+this.poi_id).html());
					});
					this.map.addOverlay(marker);
					poi.marker = marker;
					
					if (!should_not_pan_to_point)
					{
						this.map.panTo(point);
						marker.openInfoWindowHtml($("#gm_info_poi_"+id).html());
					}
				}
			}
		},
		
		show_connection: function(connection_id, should_hide)
		{
			should_hide = should_hide === true ? true : false;
			if (!this.connections[connection_id])
			{
				var connection = this.lvb_connections[connection_id];
				this.prepare_connection(connection, connection_id);
			}
			
			var connection = this.connections[connection_id];
			if (!connection.showing && !should_hide)
			{
				// first hide other connections
				for (var other_connection_id in this.connections)
				{
					if (other_connection_id == connection_id)
					{
						continue; // skip current connection
					}
					var other_connection = this.connections[other_connection_id];
					if (other_connection.showing)
					{
						this.show_connection(other_connection_id, true);
					}
				}
				
				for (var i = connection.markers.length - 1; i >= 0; i--)
				{
					var marker = connection.markers[i];
					this.map.addOverlay(marker);
				}
				for (var i = connection.lines.length - 1; i >= 0; i--)
				{
					var line = connection.lines[i];
					this.map.addOverlay(line);
				}
				connection.showing = true;
			}
			
			if(should_hide)
			{
				for (var i = connection.markers.length - 1; i >= 0; i--)
				{
					var marker = connection.markers[i];
					this.map.removeOverlay(marker);
				}
				for (var i = connection.lines.length - 1; i >= 0; i--)
				{
					var line = connection.lines[i];
					this.map.removeOverlay(line);
				}
				connection.showing = false;
			}
			else
			{
				var info = this.pan_and_zoom_map_to_points(
					[],
					connection.min_lat,
					connection.min_lng,
					connection.max_lat,
					connection.max_lng);
				this.map.setZoom(info.zoom);
				this.map.panTo(info.center);
			}
		},
		
		show_station: function(station)
		{
			if (this.temp_info_point) {
				this.map.removeOverlay(this.temp_info_point);
			}
			
			if (!station.lat || !station.lng)
			{
				return;
			}
			
			var point = new google.maps.LatLng(station.lat, station.lng);
			var marker = new google.maps.Marker(point, this.icon);
			marker.station_id = station.id;

			GEvent.addListener(marker, "click", function() {
				this.openInfoWindowHtml($("#gm_info_station_"+this.station_id).html());
			});
			this.map.addOverlay(marker);
			this.map.panTo(point);
			marker.openInfoWindowHtml($("#gm_info_station_"+station.id).html());
		},
		
		show_info_point: function(data)
		{
			if (this.temp_info_point) {
				this.map.removeOverlay(this.temp_info_point);
			}
			if (data.lat == 0 || data.lng == 0)
			{
				return;
			}
			var point = new google.maps.LatLng(data.lat, data.lng);
			var marker = new google.maps.Marker(point);
			this.map.addOverlay(marker);
			this.map.panTo(point);
			this.temp_info_point = marker;
		},
		
		// actually toggles
		show_report: function(report_id, force_show, do_not_center_on_point)
		{
			if (this.temp_info_point)
			{
				this.map.removeOverlay(this.temp_info_point);
			}

			if (!this.lvb_reports)
			{
				return;
			}
			
			if (!this.reports)
			{
				this.reports = {};
			}
			
			if (report_id != 0 && !report_id)
			{
				for (var id in this.lvb_reports)
				{
					this.show_report(id, true, true);
				}
				return;
			}
			
			if (this.reports[report_id])
			{
				var marker = this.reports[report_id];
				marker.openInfoWindowHtml($("#gm_info_report_"+report_id).html(), {maxWidth: 350});
				return;
			}

			var report = this.lvb_reports[report_id];
			
			var point = new google.maps.LatLng(report.lat, report.lng);

			var marker = new google.maps.Marker(point, {icon: this.report_icon});
			marker.report_id = report_id;
			
			this.reports[report_id] = marker;

			GEvent.addListener(marker, "click", function() {
				this.openInfoWindowHtml($("#gm_info_report_"+this.report_id).html(), {maxWidth: 350});
			});
			
			this.map.addOverlay(marker);
			if (!do_not_center_on_point)
			{
				this.map.panTo(point);
			}
		},
		
		show_ticket: function(connection_id)
		{
			var connection = this.lvb_connections[connection_id];
			var dataString = JSON.stringify(connection);

			var that = this;

			var data = {};
			data[this.lvb_maps_results_var+"[function]"] = 'ws_ticket_recommendation';
			data[this.lvb_maps_results_var+"[json_data]"] = dataString;

			$.post("?ws_ticket_recommendation", data, function(ticket_data) {
				if (!that.check_response_for_error(ticket_data))
				{
					return;
				}
				// TODO: Ticket recommendation output
				$("#dialog_content").html(ticket_data.html);
				$("#dialog").dialog("option", "title", "Mögliche Tarife");
				$("#dialog").dialog("option", "width", 450);
				$("#dialog").dialog("open");
			}, "json");
		},
		
		show_specific_line: function(line, type, direction)
		{
			if (this.temp_info_point) {
				this.map.removeOverlay(this.temp_info_point);
			}

			if (!direction)
			{
				direction = 1;
			}
			
			if (/^N/.exec(line))
			{
				type = 'nl';
			}
			
			var connection_id = 'NET_LINE_'+line+'_TYPE_'+type+'_DIR_'+direction;
			
			if (this.connections[connection_id])
			{
				this.show_connection(connection_id);
				return;
			}
			
			// load line data
			var data = {};
			data[this.lvb_maps_results_var+"[function]"] = 'ws_route_for_line';
			data[this.lvb_maps_results_var+"[line]"]      = line;
			data[this.lvb_maps_results_var+"[type]"]      = type;
			data[this.lvb_maps_results_var+"[direction]"] = direction;
			
			var that = this;
			
			$.post("?ws_route_for_line,"+line+","+type+","+direction, data, function(line_data) {
				if (!that.check_response_for_error(line_data))
				{
					return;
				}
				var connection_id = 'NET_LINE_'+line_data.line+'_TYPE_'+line_data.type+'_DIR_'+line_data.direction;
				if (!line_data.connection)
				{
					$("#dialog").dialog("option", "title", "Hinweis/Fehler");
					$('#dialog_content').text('Konnte keine Verbindung finden für Linie ' + line);
					$("#dialog").dialog("open");
					return;
				}
				if (!that.connections[connection_id])
				{
					that.prepare_connection(line_data.connection, connection_id);
				}
				that.more_station_info(line_data.station_info_html);
				that.show_connection(connection_id);
			}, "json");
			// 
		},
		
		from_here: function(input, lat, lng)
		{
			var value = typeof input == "object" ? $(input).text() : input;
			if ($("#from").is("select"))
			{
				$("#from").append('<option selected="selected" value="'+decodeURI(value)+'">'+decodeURI(value)+'</option>');
			}
			else
			{
				$("#from").val(decodeURI(value));
			}
			$("#from_value").val('');

			if (lat && lng) {
				$("#from_lat").val(lat);
				$("#from_lng").val(lng);
				$("#radio_from_opt_2").attr('checked', 'checked')
			}
			else
			{
				$("#radio_from_opt_1").attr('checked', 'checked')
			}
			
			if (!$(".tab.connection .tab_trigger").hasClass('on'))
			{
				$(".tab .tab_content").addClass('initially_hidden');
				$(".sideNavigation .tab_trigger").removeClass('on');
				$(".tab.connection .tab_content").removeClass('initially_hidden');
				$(".tab_connection").addClass('on');
			}
		},
		
		to_here: function(input, lat, lng)
		{
			var value = typeof input == "object" ? $(input).text() : input;
			if ($("#to").is("select"))
			{
				$("#to").append('<option selected="selected" value="'+decodeURI(value)+'">'+decodeURI(value)+'</option>');
			}
			else
			{
				$("#to").val(decodeURI(value));
			}
			$("#to_value").val('');
			if (lat && lng) {
				$("#to_lat").val(lat);
				$("#to_lng").val(lng);
				$("#radio_to_opt_2").attr('checked', 'checked')
			}
			else
			{
				$("#radio_to_opt_1").attr('checked', 'checked')
			}

			if (!$(".tab.connection .tab_trigger").hasClass('on'))
			{
				$(".tab .tab_content").addClass('initially_hidden');
				$(".sideNavigation .tab_trigger").removeClass('on');
				$(".tab.connection .tab_content").removeClass('initially_hidden');
				$(".tab_connection").addClass('on');
			}
		},
		
		// clear_from_coords: function()
		// {
		// 	$("#from_lat").val(0);
		// 	$("#from_lng").val(0);
		// },
		// 
		// clear_to_coords: function()
		// {
		// 	$("#to_lat").val(0);
		// 	$("#to_lng").val(0);
		// },
		
		prepare_connection: function(connection, connection_id)
		{
			var markers = [];
			var lines  = [];
			var min_lat = 180;
			var min_lng = 180;
			var max_lat = -180;
			var max_lng = -180;
			
			var previous_section_type = null;
			for (var i = 0; i < connection.sections.length; i++) {
				var is_first_section = i == 0;
				var is_last_section  = i == connection.sections.length - 1;

				var section_points = [];
				
				var section = connection.sections[i];
				
				for (var j = 0; j < section.route.length; j++)
				{
					var station = section.route[j];

					min_lat = station.lat < min_lat ? station.lat : min_lat;
					min_lng = station.lng < min_lng ? station.lng : min_lng;
					max_lat = station.lat > max_lat ? station.lat : max_lat;
					max_lng = station.lng > max_lng ? station.lng : max_lng;
					var point = new google.maps.LatLng(station.lat, station.lng);
					section_points.push(point);
					
					if (!station.id)
					{
						continue;
					}
					
					var is_first_station = j == 0;
					var is_last_station  = j == section.route.length - 1;
					var marker_icon  = this.icon;
					var marker_icon2 = null;
					if (is_first_section && is_first_station)
					{
						marker_icon2 = marker_icon;
						marker_icon  = this.icon_start;
					}
					else if (station.is_via)
					{
						marker_icon2 = marker_icon;
						marker_icon  = this.icon_via;
					}
					else if (is_last_section && is_last_station)
					{
						marker_icon2 = marker_icon;
						marker_icon  = this.icon_ziel;
					}
					else if (
						!is_first_section &&
						is_first_station &&
						section.type != "WALK" &&
						previous_section_type != "WALK"
					)
					{
						marker_icon = this.icon_umsteigen;
					}
					
					if (section.type == 'WALK')
					{
						marker_icon2 = null;
					}

					var marker = new google.maps.Marker(point, marker_icon);
					marker.station_id = station.id;
					if (is_first_section && is_first_station)
					{
						marker.station = section.from.station;
					}
					else if (is_last_section && is_last_station)
					{
						marker.station = section.to.station;
					}
					GEvent.addListener(marker, "click", function() {
						this.openInfoWindowHtml($("#gm_info_station_"+this.station_id).html());
					});
					markers.push(marker);
					
					if (marker_icon2)
					{
						var marker = new google.maps.Marker(point, marker_icon2);
						marker.station_id = station.id;
						GEvent.addListener(marker, "click", function() {
							this.openInfoWindowHtml($("#gm_info_station_"+this.station_id).html());
						});
						markers.push(marker);
					}
				}
				
				var line = new google.maps.Polyline(
					section_points,
					section.color,
					5,  // thinkness
					0.85, // alpha
					{clickable: false}
				);
				
				lines.push(line);
				previous_section_type = section.type;
			}
			this.connections[connection_id] = {showing: false, markers: markers, lines: lines, min_lat: min_lat, min_lng: min_lng, max_lat: max_lat, max_lng: max_lng};
		},
		
		schedule_more: function(direction)
		{
			var data = {};
			data[this.lvb_maps_results_var+"[function]"]     = 'ws_schedule_more';
			data[this.lvb_maps_results_var+"[direction]"]    = direction;
			data[this.lvb_maps_results_var+"[context]"]      = this.lvb_maps_context;
			
			var that = this;
			
			$.post("?schedule_more,"+direction, data, function(data) {
				var connections = data.connections;
				var html        = data.html;
				var direction   = data.direction;
				var tbody       = $('#routing_container table.connection_table tbody.connection_tbody');

				that.lvb_maps_context = data.context;
				
				if(direction < 0)
					tbody.prepend(html);
				else
					tbody.append(html);
				
				for (var connection_id in connections)
				{
					var connection = connections[connection_id];
					that.prepare_connection(connection, connection_id);
					
					$('tbody.connection_tbody tr#' + connection_id).bind('click', function(){
						var connection_row = $(this);
						var id = connection_row.attr('id');
						app.show_connection(id);
						$('tbody.connection_tbody tr.results').not($('#connection_details_for_' + id + ' tr.results')).addClass('initially_hidden').find('a.toggle').text('öffnen').removeClass('close').addClass('open');
						$('tbody.connection_tbody tr.interstation').not('#connection_details_for_' + id + ' tr.interstation').addClass('initially_hidden');
						$('#connection_details_for_' + id).removeClass('initially_hidden');
						connection_row.removeClass('open').addClass('close');
						$('tbody.connection_tbody tr.connection_row').not(connection_row).removeClass('close').addClass('open');
						var toggle = $(this).find('td.station a.toggle').removeClass('open').addClass('close').text('schließen');
						$('tbody.connection_tbody td.station a.toggle').not(toggle).removeClass('close').addClass('open').text('öffnen');
						return false;
					});
					
					$('tbody.connection_tbody tr#connection_details_for_' + connection_id + ' a.toggle').parents('tr.results tr').bind('click', function(){
						var toggle = $(this).find('a.toggle');
						$('tr.interstation_for_' + toggle.attr('id')).toggleClass('initially_hidden');
						if(toggle.hasClass('open'))
							toggle.find('a.toggle').removeClass('open').addClass('close').text('schließen');
						else
							toggle.find('a.toggle').removeClass('close').addClass('open').text('öffnen');
						return false;
					});
				}
				
				that.more_station_info(data.station_info_html);
			}, "json");
		},
		
		more_station_info: function(station_info)
		{
			for (var station_key in station_info)
			{
				var info = station_info[station_key];
				if ($('#gm_info_station_'+station_key).get().length == 0)
				{
					$('#gm_info').append(info);
				}
			}
		},		
		
		pan_and_zoom_map_to_points: function(points, min_lat, min_lng, max_lat, max_lng)
		{
			if (!min_lat) { min_lat = 180; }
			if (!min_lng) { min_lng = 90; }
			if (!max_lat) { max_lat = -180; }
			if (!max_lng) { max_lng = -90; }
			
			$.each(points, function(i, point) {
				if (point.lat() < min_lat) { min_lat = point.lat(); }
				if (point.lng() < min_lng) { min_lng = point.lng(); }
				if (point.lat() > max_lat) { max_lat = point.lat(); }
				if (point.lng() > max_lng) { max_lng = point.lng(); }
			});
			
			var bounds = new google.maps.LatLngBounds(
				new google.maps.LatLng(min_lat, min_lng),
				new google.maps.LatLng(max_lat, max_lng));
			var zoom = Math.min(this.map.getBoundsZoomLevel(bounds), 17);

			var center_lat = (max_lat + min_lat) / 2;
			var center_lng = (max_lng + min_lng) / 2;
			var center = new google.maps.LatLng(center_lat, center_lng);
			
			return {zoom: zoom, center: center};
		},
		
		trigger_find_connection: function() {
			$("#form_connection").submit();
		},
		
		trigger_info_stop: function() {
			$("#form_stop").submit();
		},
		
		prepare_find_connections: function(override)
		{
			var from = $("#from").val();
			var to   = $("#to").val();
			
			var has_errors = false;
			if ($("#from").is('input') && from.length < this.minimum_input_length)
			{
				this.show_errors("from", "invalid");
				has_errors = true;
			}
			else
			{
				this.show_errors("from");
			}

			if ($("#to").is('input') && to.length < this.minimum_input_length)
			{
				this.show_errors("to", "invalid");
				has_errors = true;
			}
			else
			{
				this.show_errors("to");
			}
			
			if (!has_errors)
			{
				this.find_connections(JSON.stringify($("#form_connection").serializeArray()));
			}
		},
		
		toggle_timetables: function(item)
		{
			item = $(item);
			if(item.val().length > 0)
			{
				var timetable_id = "#timetable_direction_" + item.val();
				var timetable_show = $(timetable_id);
				var timetables = $(".timetable_direction");
				if(timetable_show.length > 0 && timetables.length > 0)
				{
					timetables.addClass("hideme");
					timetable_show.removeClass("hideme");
				}
			}
		},
		
		find_timetables: function(item)
		{
			var that = this;
			item = $(item);
			if (item.val().length > 0)
			{
				var data    = {};
				data[this.lvb_maps_results_var+"[function]"] = "ws_find_timetables";
				var search  = {};
				var obj     = null;
				var sep_pos = null;
				
				if (item.attr('class') == 'timetable_changes_select' || item.attr('id') == 'timetable_date')
				{
					select_list = $("#form_timetable select.timetable_select");
					select_list.each(function()
					{
						$(this).find('option').each(function()
						{
							if($(this).attr("selected") && obj == null && $(this).val() != "NULL")
							{
								obj = $(this).val();
							};
						});
					});				
					
					if (obj == null)
					{
						return true;
					}
					
					sep_pos        = obj.indexOf("_");
					search["type"] = obj.substring(0,sep_pos);
					search["line"] = obj.substring(sep_pos+1);
					
					if (item.attr('class') == 'timetable_changes_select')
					{
						search["change"] = item.find("option:selected").val();
					}
				}
				else
				{
					sep_pos 	   = item.val().indexOf("_");
					search["type"] = item.val().substring(0,sep_pos);
					search["line"] = item.val().substring(sep_pos+1);
				}
				
				search["date"] = $("#timetable_date").val();
				data[this.lvb_maps_results_var+"[data]"]     = JSON.stringify(search);
				$("div.tab.timetable #stop_timetable_content").load("?ws_find_timetables", data, function(){
					window.setTimeout(function(){
						$("#timetable_directions").bind("change", function(){
							that.toggle_timetables(this);
							return false;
						});
						$(".timetable_changes_select").bind("change", function(){
							that.find_timetables(this);
							return false;
						});
						$("#form_timetable").find("select.timetable_select").bind("change", function(){
							var current_select = $(this);
							if(current_select.val() != "NULL" && current_select.length > 0)
							{
								var selects = $("#form_timetable").find("select.timetable_select:not(#" + current_select.attr('id') + ")");
								selects.each(function(){
									$(this).find("option:eq(0)").attr("selected","selected");
								})
								that.find_timetables(this);
							}
							return false;
						});			
						$("#timetable_date").bind("change", function(){
							that.find_timetables(this);
							return false;
						});			
						$("input.date").datepicker();
					}, 150);
				});
			}
		},
		
		show_errors: function (name, code)
		{
			if (code)
			{
				code = this.error_messages[code] ? code : "generic";
				var lang = GLanguage.getLanguageCode();
				$("#"+name+"_error").text(this.error_messages[code][lang]).removeClass("hideme").addClass("err");
				$("#"+name).addClass("has_error");
			}
			else
			{
				$("#"+name).removeClass("has_error");
			}
		},
		
		modify_search: function(serialized_form_data)
		{
			var that = this;
			var data = {};
			data[this.lvb_maps_results_var+"[function]"] = "ws_modify_search";
			data[this.lvb_maps_results_var+"[data]"]     = JSON.stringify($("#form_connection").serializeArray());
			$('div.tab.connection .tab_content').load("?ws_modify_search", data, function() {
				window.setTimeout(function(){
					$("#form_connection").bind("submit", function(){
						that.prepare_find_connections();
						return false;
					});
					$("#trigger_modify_search").click(function() {
						that.modify_search();
					});
					$("input.date").datepicker();
					$("input.time").timeEntry({spinnerImage: false});
					that.fix_select_width();
				}, 150);
			});
		},
		
		modify_search_stop: function(serialized_form_data)
		{
			var that = this;
			var data = {};
			data[this.lvb_maps_results_var+"[function]"] = "ws_modify_search_stop";
			data[this.lvb_maps_results_var+"[data]"]     = JSON.stringify($("#form_stop").serializeArray());
			$('div.tab.stop .tab_content').load("?ws_modify_search_stop", data, function() {
				window.setTimeout(function(){
					$("#form_stop").bind("submit", function(){
						that.find_schedule(JSON.stringify($(this).serializeArray()));
						return false;
					});
					$("#trigger_modify_search_stop").click(function() {
						that.modify_search_stop();
					});
					$("input.date").datepicker();
					$("input.time").timeEntry({spinnerImage: false});					
					that.fix_select_width();
				}, 150);
			});
		},
		
		find_connections: function(serialized_form_data)
		{
			var data = {};
			data[this.lvb_maps_results_var+"[function]"] = "ws_find_connections";
			data[this.lvb_maps_results_var+"[data]"]     = serialized_form_data;

			var that = this;

			$.ajax({
				url: "?ws_find_connections",
				type: "POST",
				data: data,
				dataType: "json",
				cache: false,
				success: function(connection_data, textStatus, XMLHttpRequest) {
					if (!that.check_response_for_error(connection_data))
					{
						that.clear_connections();
						return;
					}
					var first_connection_id;
					that.lvb_maps_context = connection_data.context;
				
					that.clear_connections();
				
					for (var connection_id in connection_data.connections)
					{
						if (!first_connection_id)
						{
							first_connection_id = connection_id;
						}
						that.prepare_connection(connection_data.connections[connection_id], connection_id);
					
						that.lvb_connections[connection_id] = connection_data.connections[connection_id];
					}
					$("div.tab.connection .tab_content").html(connection_data.html);
					$("#routing_container").html(connection_data.connections_html);
					$("#routing_container").removeClass("initially_hidden");
					$("tbody.connection_tbody tr.connection_row").bind('click', function(){
						var row = $(this);
						app.show_connection(row.attr('id'));
						$('tbody.connection_tbody tr.results').not($('#connection_details_for_' + row.attr('id') + ' tr.results')).addClass('initially_hidden').find('a.toggle').text('öffnen').removeClass('close').addClass('open');
						$('tbody.connection_tbody tr.interstation').not('#connection_details_for_' + row.attr('id') + ' tr.interstation').addClass('initially_hidden');
						$('#connection_details_for_' + row.attr('id')).removeClass('initially_hidden');
						row.removeClass('open').addClass('close');
						$('tbody.connection_tbody tr.connection_row').not(row).removeClass('close').addClass('open');
						var toggle = row.find('td.station a.toggle').removeClass('open').addClass('close').text('schließen');
						$('tbody.connection_tbody td.station a.toggle').not(toggle).removeClass('close').addClass('open').text('öffnen');
						return false;
					});
					$('tbody.connection_tbody tr.results a.toggle').parents('tr.results tr').bind('click', function(){
						trigger = $(this).find('a.toggle');
						$('tr.interstation_for_' + trigger.attr('id')).toggleClass('initially_hidden');
						if(trigger.hasClass('open'))
							trigger.removeClass('open').addClass('close').text('schließen');
						else
							trigger.removeClass('close').addClass('open').text('öffnen');
						return false;
					});
					that.more_station_info(connection_data.station_info_html);
					that.show_connection(first_connection_id);
					$('#connection_details_for_'+first_connection_id).removeClass('initially_hidden');
					window.setTimeout(function(){
						$("#form_connection").bind("submit", function(){
							that.prepare_find_connections();
							return false;
						});
						$("#trigger_modify_search").click(function() {
							that.modify_search();
						});
						that.fix_select_width();
						$.timeEntry.setDefaults($.timeEntry.regional['de']);
						$("input.time").timeEntry({spinnerImage: false});

						$.datepicker.setDefaults($.datepicker.regional['de']);
						$("input.date").datepicker();
						
						changeLabel();
					}, 150);
				},
				error: function(_request, textStatus, errorThrown) {
					that.clear_connections();
					$('div.tab.connection .tab_content').html(_request.responseText);
					window.setTimeout(function(){
						$("#form_connection").bind("submit", function(){
							that.prepare_find_connections();
							return false;
						});
						$("#trigger_modify_search").click(function() {
							that.modify_search();
						});
						that.fix_select_width();
						$.timeEntry.setDefaults($.timeEntry.regional['de']);
						$("input.time").timeEntry({spinnerImage: false});

						$.datepicker.setDefaults($.datepicker.regional['de']);
						$("input.date").datepicker();
						
						changeLabel();
					}, 150);
				}
			});
		},
		
		clear_connections: function()
		{
			for (var connection_id in this.connections)
			{
				this.show_connection(connection_id, true);
			}
			this.connections = [];
		},
		
		find_schedule: function(serialized_form_data)
		{
			var data = {};
			data[this.lvb_maps_results_var+"[function]"] = "ws_info_stop";
			data[this.lvb_maps_results_var+"[data]"]     = serialized_form_data;

			var that = this;

			$.ajax({
				url: "?ws_info_stop",
				type: "POST",
				data: data,
				dataType: "json",
				cache: false,
				success: function(stop_data, textStatus, XMLHttpRequest) {
					if (!that.check_response_for_error(stop_data))
					{
						return;
					}
					that.more_station_info(stop_data.station_info_html);
					that.show_station(stop_data.station_info);

					$('#stop_tab_content').html(stop_data.html);
					$("#form_stop").bind("submit", function(){
						that.find_schedule(JSON.stringify($(this).serializeArray()));
						return false;
					});
					$("#trigger_modify_search_stop").click(function() {
						that.modify_search_stop();
					});						
					setTimeout(function(){
						$.timeEntry.setDefaults($.timeEntry.regional['de']);
						$("input.time").timeEntry({spinnerImage: false});

						$.datepicker.setDefaults($.datepicker.regional['de']);
						$("input.date").datepicker();
					}, 150);
					
				},
				error: function(_request, textStatus, errorThrown) {
					$('#stop_tab_content').html(_request.responseText);
					$("#form_stop").bind("submit", function(){
						that.find_schedule(JSON.stringify($(this).serializeArray()));
						return false;
					});
					$("#trigger_modify_search_stop").click(function() {
						that.modify_search_stop();
					});						
					setTimeout(function(){
						$.timeEntry.setDefaults($.timeEntry.regional['de']);
						$("input.time").timeEntry({spinnerImage: false});

						$.datepicker.setDefaults($.datepicker.regional['de']);
						$("input.date").datepicker();
					}, 150);
				}
			});
		},
		
		check_response_for_error: function(response)
		{
			if (response.error)
			{
				$("#dialog").dialog("option", "title", "Hinweis/Fehler");
				$("#dialog_content").html(response.error);
				$("#dialog").dialog("open");
				return false;
			}
			
			if ($("#dialog").dialog("isOpen"))
			{
				$("#dialog").dialog("close");
			}
			return true;
		},
		
		add_connection_to_mylvb: function()
		{
			var data = {};
			data[this.lvb_maps_results_var+"[function]"] = "ws_add_to_mylvb";
			data[this.lvb_maps_results_var+"[data]"]     = JSON.stringify($("#form_connection").serializeArray());
			
			var that = this;
			$.post('?ws_add_to_mylvb', data, function(mylvb_data){
				if (!that.check_response_for_error(mylvb_data))
				{
					return;
				}
				$('#connection_add_to_mylvb_content').html(mylvb_data.html);
				if (window.loadResults)
				{
					window.loadResults(true);
				}
			}, "json");
			
		},
		
		add_monitor_to_mylvb: function()
		{
			var data = {};
			data[this.lvb_maps_results_var+"[function]"] = "ws_add_to_mylvb";
			data[this.lvb_maps_results_var+"[data]"]     = JSON.stringify($("#form_stop").serializeArray());
			
			var that = this;
			$.post('?ws_add_to_mylvb', data, function(mylvb_data){
				if (!that.check_response_for_error(mylvb_data))
				{
					return;
				}
				$('#monitor_add_to_mylvb_content').html(mylvb_data.html);
				if (window.loadResults)
				{
					window.loadResults(true);
				}
			}, "json");
			
		},
		
		show_line_net_plan: function()
		{
			if (!GBrowserIsCompatible())
			{
				return;
			}
			
			$("#map_lnp").show();
			if (!this.map_lnp)
			{
				var that = this;
				var width_day  = 7057;
				var height_day = 4985;
				this.tiles_day = {
					"2": [3,2],
					"3": [6,4],
					"4": [13,9],
					"5": [27,19]
				}
				this.tiles_night = {
					"2": [3,2],
					"3": [7,5],
					"4": [15,10],
					"5": [31,20]
				}

				var lnp_min_zoom = 2;
				var lnp_max_zoom = 5;

				var lnp_layer_day = new google.maps.TileLayer(
					new google.maps.CopyrightCollection("(c) LVB 2009"), lnp_min_zoom, lnp_max_zoom);
				var lnp_layer_night = new google.maps.TileLayer(
					new google.maps.CopyrightCollection("(c) LVB 2009"), lnp_min_zoom, lnp_max_zoom);

				lnp_layer_day.getCopyright   = function(a,b) {
					return {prefix:"(c) LVB", copyrightTexts:["Copyrighted"]};
				};
				lnp_layer_night.getCopyright = function(a,b) {
					return {prefix:"(c) LVB", copyrightTexts:["Copyrighted"]};
				};

				lnp_layer_day.isPng   = function() {return false;};
				lnp_layer_night.isPng = function() {return false;};

				lnp_layer_day.getOpacity   = function() {return 1.0;}; 
				lnp_layer_night.getOpacity = function() {return 1.0;};

				lnp_layer_day.getTileUrl = function(point, zoom)
				{
					return that.asset_url + "/images/lnp/day/" + zoom + "/" + point.x + "/" + (that.tiles_day[zoom][1]-point.y) + ".jpg";
				};

				lnp_layer_night.getTileUrl = function(point, zoom)
				{
					return that.asset_url + "/images/lnp/night/" + zoom + "/" + point.x + "/" + (that.tiles_night[zoom][1]-point.y) + ".jpg";
				};

				var projection_day = {
					fromLatLngToPixel: function (latlng, zoom)
					{
						var factor = 1/Math.pow(2, lnp_max_zoom - zoom);
						return new google.maps.Point(latlng.lat()*100*factor, latlng.lng()*100*factor);
					},
					fromPixelToLatLng: function(pixel, zoom, unbounded)
					{
						var factor = 1/Math.pow(2, lnp_max_zoom - zoom);
						return new google.maps.LatLng(pixel.x/100/factor, pixel.y/100/factor);
					},
					tileCheckRange: function(tile, zoom, tilesize)
					{
						return tile.y >= 0 && tile.x >= 0 && tile.x <= that.tiles_day[zoom][0] && tile.y <= that.tiles_day[zoom][1];
					},
					getWrapWidth: function(zoom) {
						return 99999999999;
					}
				};
				var projection_night = {
					fromLatLngToPixel: function (latlng, zoom)
					{
						var factor = 1/Math.pow(2, lnp_max_zoom - zoom);
						return new google.maps.Point(latlng.lat()*100*factor, latlng.lng()*100*factor);
					},
					fromPixelToLatLng: function(pixel, zoom, unbounded)
					{
						var factor = 1/Math.pow(2, lnp_max_zoom - zoom);
						return new google.maps.LatLng(pixel.x/100/factor, pixel.y/100/factor);
					},
					tileCheckRange: function(tile, zoom, tilesize)
					{
						return tile.y >= 0 && tile.x >= 0 && tile.x <= that.tiles_night[zoom][0] && tile.y <= that.tiles_night[zoom][1];
					},
					getWrapWidth: function(zoom) {
						return 99999999999;
					}
				};
				var lang = GLanguage.getLanguageCode();
				var map_type_lnp_day = new google.maps.MapType(
					[lnp_layer_day],
					projection_day,
					this.i18n.map_type_day[lang],
					{maxResolution:lnp_max_zoom, minResolution:lnp_min_zoom, errorMessage:"Keine Daten"}) ;

				var map_type_lnp_night = new google.maps.MapType(
					[lnp_layer_night],
					projection_night,
					this.i18n.map_type_night[lang],
					{maxResolution:lnp_max_zoom, minResolution:lnp_min_zoom, errorMessage:"Keine Daten"}) ;

				this.map_lnp = this.setup_google_maps("map_lnp", {backgroundColor: "#FFFFFF", mapTypes: [map_type_lnp_day, map_type_lnp_night]}, true);
				
				{
					function MyMapTypeControl() {
					}
					
					MyMapTypeControl.prototype = new GControl();
					
					MyMapTypeControl.prototype.initialize = function(map) {
						var container = document.createElement("div");

						var mapDayDiv = document.createElement("div");
						mapDayDiv.style.position ="absolute";
						mapDayDiv.style.left ="-150px";
						this.setButtonStyle_(mapDayDiv);
						container.appendChild(mapDayDiv);
						mapDayDiv.appendChild(document.createTextNode(that.i18n.map_type_day[lang]));
						GEvent.addDomListener(mapDayDiv, "click", function() {
							map.setMapType(map_type_lnp_day);
						});

						var mapNightDiv = document.createElement("div");
						mapNightDiv.style.position ="absolute";
						mapNightDiv.style.left ="-75px";
						this.setButtonStyle_(mapNightDiv);
						container.appendChild(mapNightDiv);
						mapNightDiv.appendChild(document.createTextNode(that.i18n.map_type_night[lang]));
						GEvent.addDomListener(mapNightDiv, "click", function() {
							map.setMapType(map_type_lnp_night);
						});

						map.getContainer().appendChild(container);
						return container;
					}
				}
				
				MyMapTypeControl.prototype.getDefaultPosition = function() {
					return new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(7, 7));
				}
				
				MyMapTypeControl.prototype.setButtonStyle_ = function(button) {
					button.style.color = "#333333";
					button.style.backgroundColor = "white";
					button.style.font = "small Arial";
					button.style.border = "1px solid #333333";
					button.style.padding = "2px";
					button.style.marginBottom = "3px";
					button.style.textAlign = "center";
					button.style.width = "5em";
					button.style.cursor = "pointer";
					button.style.display = "inline-block";
					button.style.marginRight = "5px";
				}
				
				this.map_lnp.addControl(new MyMapTypeControl());

				var lat = (width_day+350)  / (2*100);
				var lng = (height_day+550) / (2*100);
				this.map_lnp.setCenter(new google.maps.LatLng(lat, lng), lnp_min_zoom+1);
				this.map_lnp.setMapType(map_type_lnp_day);
				
				// GEvent.addListener(this.map_lnp_day, "movestart", function() {return false;});
				// GEvent.addListener(this.map_lnp_night, "movestart", function() {return false;});
				
			}
		},
		
		stack_report: function(report_id)
		{
			this.stacked_reports.push(report_id);
		},
		
		fix_select_width: function()
		{
			if($('select.iefix') && jQuery.browser.msie) {
	
				$('select.iefix').wrap('<div class="location" />');
				$('select.iefix').mousedown(function() {
					$(this).css('width', 'auto');
					if (parseInt($(this).parent().css('width')) > $(this).width()) {
						$(this).css('width', $(this).parent().css('width'));
					}
				}).blur(function() {
					$(this).css('width', $(this).parent().css('width'));
				}).change(function() {
					$(this).css('width', $(this).parent().css('width'));
				});
			}
			
		}
		
	};

});
