vyletnik.map = {};

vyletnik.map.google = {};

vyletnik.map.google.MapEngine = function (positionUrlSetter) {
	var gmap;
	var pointManagers = {};
	
	var dontSetToUrl = false;
	var nameWindowOpened = false;
	
	//posledni highlight marker, kvuli jeho smazani
	var activeHoveredMarker;

	function modifyPointWithHighlightEffects(point) {		
		onHoverMarker = vyletnik.map.google.createHighLightMarker(point);			
		point.mapHighlight = function (hoveredMarker, effectMarker) {
			return function () {
				if (activeHoveredMarker) {
					gmap.removeOverlay(activeHoveredMarker);
					activeHoveredMarker = null;
				}
				gmap.addOverlay(effectMarker);
				activeHoveredMarker = effectMarker;
			};
		}(point.marker, onHoverMarker);
		point.mapUnhighlight = function (hoveredMarker, effectMarker) {
			return function () {
				gmap.removeOverlay(effectMarker);
				activeHoveredMarker = null;
			};				
		}(point.marker, onHoverMarker);
		GEvent.addListener(point.marker, 'mouseover', function () {
			if (point.listHighlight) {
				point.listHighlight();
			}
			if (gmap.getInfoWindow().isHidden() === true) {
				showInfoWindowWithName(point);
				nameWindowOpened = true;
			}
		});
		GEvent.addListener(point.marker, 'mouseout', function () {
			if (point.listUnhighlight) {
				point.listUnhighlight();
			}
			if (nameWindowOpened === true) {
				point.marker.closeInfoWindow();
				nameWindowOpened = false;
			}
		});
	}	
	function resetMapPosition() {
		gmap.setCenter(new GLatLng(49.884017217246544, 15.743408203125), 8);
	}
	function focalize(minx, miny, maxx, maxy) {
		var bounds = new GLatLngBounds(
			vyletnik.map.google.getLatLng(minx, miny),
			vyletnik.map.google.getLatLng(maxx, maxy)
		);
		var zoom = gmap.getBoundsZoomLevel(bounds);
		gmap.setCenter(bounds.getCenter(), zoom);		
	}
	function zoomToPoint(point, level) {
		if (!level) {
			level = 17;
		}
		gmap.setCenter(vyletnik.map.google.getLatLng(point.x, point.y), level);
	}
	function zoomOutOfPoint() {
		gmap.setZoom(13);
	}
	var pub = {
		getInitializer : function (viewPortCriteriaModifier) {
			return {
				init : function (element) {
					var overviewControl = new GOverviewMapControl(new GSize(290, 290));
					gmap = new google.maps.Map2(element);
					gmap.enableScrollWheelZoom();
					resetMapPosition();
					gmap.addControl(new GLargeMapControl3D(), new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(0, 20)));
					gmap.addControl(overviewControl, new GControlPosition(G_ANCHOR_BOTTOM_RIGHT));
					gmap.addControl(new GScaleControl());
					gmap.addControl(new GMapTypeControl(), new GControlPosition(G_ANCHOR_TOP_LEFT));

					//nastavit ihned po inicializaci
					var bounds = gmap.getBounds();
					var swPoint = bounds.getSouthWest();
					var nePoint = bounds.getNorthEast();
					var zoom = gmap.getZoom();
					viewPortCriteriaModifier.setViewport(zoom, swPoint.lat(), swPoint.lng(), nePoint.lat(), nePoint.lng());
					
					GEvent.addListener(gmap, "moveend", function () {
						var bounds = gmap.getBounds();
						var swPoint = bounds.getSouthWest();
						var nePoint = bounds.getNorthEast();
						var zoom = gmap.getZoom();
						viewPortCriteriaModifier.setViewport(zoom, swPoint.lat(), swPoint.lng(), nePoint.lat(), nePoint.lng());
						var center = gmap.getCenter();
						if (dontSetToUrl === false) {
							positionUrlSetter.setPosition(center.lat(), center.lng(), zoom);
						}
						dontSetToUrl = false;
					});

					//@todo pokusit se o refaktor
					jQuery.each(pointManagers, function (key, obj) {
						obj.setMap(gmap);
						obj.withEachAddedPoint(modifyPointWithHighlightEffects);
					});
					
					//zjisteni kdy je inicializovana overview mapka
					(function initOverview() {
						var ovElement = document.getElementById('mapPanel_overview');
						if (ovElement) {
							overviewControl.hide();
							//jQuery('#mapPanel_overview div img').replaceWith('<img src="http://icons.iconarchive.com/icons/gordon-irving/x-set/X-Au-Blu-icon.jpg" />');
							return;
						} else {
							setTimeout(initOverview, 100);
						}
					})();
				}
			};
		},
		getFocalizer : function () {
			return {
				focalize : function (minx, miny, maxx, maxy) {
					focalize(minx, miny, maxx, maxy);
				}
			};
		},
		getFocalizerWhichDontSetUrl : function () {
			return {
				focalize : function (minx, miny, maxx, maxy) {
					dontSetToUrl = true;	
					focalize(minx, miny, maxx, maxy);
				}
			};			
		},
		getDetailZoomer : function () {
			return {
				zoomTo : function (point, level) {
					zoomToPoint(point, level);
				},
				zoomOut : function () {
					zoomOutOfPoint();
				}
			};
		},
		getDetailZoomerWhichDontSetUrl : function () {
			return {
				zoomTo : function (point, level) {
					dontSetToUrl = true;
					zoomToPoint(point, level);
				},
				zoomOut : function () {
					dontSetToUrl = true;
					zoomOutOfPoint();
				}
			};			
		},
		getPointsHandlingAgent : function (pointDetailGetter) {
			var markerCreater = vyletnik.map.google.BaseMarkerCreater(pub.getInfoWindowShower(pointDetailGetter));
			
			return {
				addPointsSettedReporter : function (reporter, namespace) {
					pointManagers[namespace] = vyletnik.map.google.PointsManager(markerCreater);
					if (gmap) {
						pointManagers[namespace].setMap(gmap);
					}					
					reporter.registerListener(pointManagers[namespace].setPoints);
				},
				addPointsClearedReporter : function (reporter, namespace) {
					reporter.registerListener(pointManagers[namespace].clearPoints);
				}
			};
		},
		/**
		 * Vrati otevirac bubliny na mape, ktery podle id bodu
		 * zjisti vsechny jeho informace a zobrazi je
		 */
		getInfoWindowShower : function (pointDetailGetter) {
			function getGpsString(l) {
				return vyletnik.utils.getGpsString(l);
			}
			
			return {
				/**
				 * Vraci zobrazovac informacni bubliny
				 * Pokud je point.type === 'adresa', zobrazi bublinu s informacemi o adrese
				 */
				show : function (point) {
					var latlng = vyletnik.map.google.getLatLng(point.x, point.y);
					if (point.type === 'address') {
						var info = point.name.split(', ');
						var content = '<div class="infoWindowTab"><h1>' + info[0] + '</h1></div>';
						for (var i = 1; i < info.length; i++) {
							content += '<br/>' + info[i];
						}
						point.marker.openInfoWindowHtml(content);
					} else {
						/**
						 * kdo bude obstaravat data pro bublinu
						 * 1. obecny detailGetter
						 * 2. detail getter prirazeny primo k bodu
						 */
						var detailGetter = pointDetailGetter;
						if (point.detailGetter) {
							detailGetter = point.detailGetter;
						}
						detailGetter.withDetail(point.type, point.id, function (tabs) {
							var infoTab = tabs.infoTab;
							var photoTab = tabs.photoTab;
							var gpsTab = tabs.gpsTab;
							var linksTab = tabs.linksTab;
							
							jQuery(gpsTab).find('.lat').html(getGpsString(latlng.lat()));
							jQuery(gpsTab).find('.lng').html(getGpsString(latlng.lng()));
							
							var gTabs = [];
							gTabs[gTabs.length] = new GInfoWindowTab('Info', infoTab);
							if (photoTab !== null) {
								gTabs[gTabs.length] = new GInfoWindowTab('Foto', photoTab);
							}
							gTabs[gTabs.length] = new GInfoWindowTab('GPS', gpsTab);
							gTabs[gTabs.length] = new GInfoWindowTab('Odkazy', linksTab);
							point.marker.openInfoWindowTabsHtml(gTabs);
						});
					}
					//zavre pripadne oteverene okno s nazvem, tak mu to musime sdelit
					nameWindowOpened = false;					
					centerByInfoWindow(latlng);
				}
			};
		},
		getHistoryChangeListener : function () {
			return function (params) {
				if (params.isChanged(['x', 'y', 'z'])) {
					var x = params.getString('x');
					var y = params.getString('y');
					var zoom = params.getString('z');
					if (x.length > 0) {
						dontSetToUrl = true;
						gmap.setCenter(new GLatLng(x, y), parseInt(zoom, 10));
					} else {
						dontSetToUrl = true;
						resetMapPosition();
					}
				}
			};			
		},
		getClickReporter : function () {
			var clickEvent = vyletnik.utils.Event();
			GEvent.addListener(gmap, "click", function (overlay, latlng, overlaylatlng) {
				clickEvent.notifyListeners({lat : latlng.lat(), lng : latlng.lng()});
			});
			return {
				addListener : function (listener) {
					clickEvent.registerListener(listener);
				}
			};
		},
		getOkoliBoundsGetter : function () {
			var centerMarker;
			
			return {
				getCenterx : function () {
					return centerMarker.getLatLng().lat();
				},
				getCentery : function () {
					return centerMarker.getLatLng().lng();
				},
				initMarker : function () {
					if (!centerMarker) {
						centerMarker = new GMarker(gmap.getCenter(), {title : 'Střed hledání', draggable : true});
						gmap.addOverlay(centerMarker);
					}
				},
				disableMarker : function () {
					if (centerMarker) {
						gmap.removeOverlay(centerMarker);
						centerMarker = null;
					}
				}
			};
		},
		getBoundsDrawer : function () {
			var lastCircle;
			var pub = {
				showLocation : function (x, y, distance) {					
					var projection = gmap.getCurrentMapType().getProjection();
					var center = new GLatLng(x, y);
					
					var bottomPoint = getPointFrom(center.latRadians(), center.lngRadians(), 180 , distance);
					var swPoint = getPointFrom(bottomPoint.latRadians(), bottomPoint.lngRadians(), 270 , distance);
							
					var topPoint = getPointFrom(center.latRadians(), center.lngRadians(), 0, distance);
					var nePoint = getPointFrom(topPoint.latRadians(), topPoint.lngRadians(), 90, distance);

					var zoomToFit = gmap.getBoundsZoomLevel(new GLatLngBounds(swPoint, nePoint));
					var size = projection.fromLatLngToPixel(bottomPoint, zoomToFit).y - projection.fromLatLngToPixel(topPoint, zoomToFit).y;

					dontSetToUrl = true;
					gmap.setCenter(new GLatLng(x, y), zoomToFit);

					if (lastCircle) {
						gmap.removeOverlay(lastCircle);
					}
					var circle = new EInsert(new GLatLng(x, y),
							"/public/images/gmap-circle.png",
							new GSize(size, size), zoomToFit);				
					gmap.addOverlay(circle);
					lastCircle = circle;
				},
				removeCircle : function () {
					if (lastCircle) {
						gmap.removeOverlay(lastCircle);
					}
				}
			};
			
			function getPointFrom(lat1, lon1, brng, d) {
				function toDeg(num) {
					return num * 180 / Math.PI;
				}
				function toRad(num) {
					return num * Math.PI / 180;
				}
				
				brng = toRad(brng);
				
				var R = 6371;
				var lat2 = Math.asin(Math.sin(lat1) * Math.cos(d / R) + Math.cos(lat1) * Math.sin(d / R) * Math.cos(brng));
				var lon2 = lon1 + Math.atan2(Math.sin(brng) * Math.sin(d / R) * Math.cos(lat1), Math.cos(d / R) - Math.sin(lat1) * Math.sin(lat2));
				return new GLatLng(toDeg(lat2), toDeg(lon2));
			}
			
			return pub;	
		}
	};
	function showInfoWindowWithName(point) {
		point.marker.openInfoWindow('<table style="height:58px;width:217px"><tr><td style="height:100%;width:100%;vertical-align:middle;">' + point.name + '</td></tr></table>');
	}
	function centerByInfoWindow(latlng) {
		//za pomoci projekce prepocitame umisteni bodu z px na latlng
		//cilem je vycentrovat mapu ne podle bodu, ale podle bubliny
		var projection = gmap.getCurrentMapType().getProjection();
		var zoom = gmap.getZoom();
		pointCenterPixels = projection.fromLatLngToPixel(latlng, zoom);
		pointCenterPixels.x = pointCenterPixels.x + 191;
		pointCenterPixels.y = pointCenterPixels.y - 137;
		gmap.setCenter(projection.fromPixelToLatLng(pointCenterPixels, zoom));
	}
	return pub;
};

vyletnik.map.google.PointsManager = function (markerCreator) {
	var loadedPoints = {};
	var pointModifier;
	var gmap;
	
	var pub = {
		setPoints : function (points) {
			tidyUpPoints(points);
			addNewPoints(points);
		},
		clearPoints : function () {
			tidyUpPoints({});
		},
		withEachAddedPoint : function (handler) {
			pointModifier = handler;
		},
		setMap : function (pgmap) {
			gmap = pgmap;
		}
	};
	
	function tidyUpPoints(pointsToSee) {
		var hashedPointsToSee = hashPoints(pointsToSee);		
		jQuery.each(loadedPoints, function (hash, point) {
			if (!hashedPointsToSee[hash]) {
				removePoint(hash);
			}
		});
	}
	function addNewPoints(points) {
		for (var i = 0; i < points.length; i++) {
			point = points[i];
			if (isPointLoaded(point) === false) {
				addPoint(point);
			}
		}
	}
	function addPoint(point) {
		var marker = markerCreator.createMarker(point);
		loadPoint(point, marker);
		if (pointModifier) {
			pointModifier(point);
		}
		gmap.addOverlay(marker);
	}
	function loadPoint(point, marker) {
		loadedPoints[hashPoint(point)] = point;
		point.marker = marker;
	}
	function removePoint(hash) {
		gmap.removeOverlay(loadedPoints[hash].marker);
		delete loadedPoints[hash];
	}
	function hashPoint(point) {
		return point.type + point.id;
	}	
	function hashPoints(points) {
		var hashedPoints = {};
		for (var i = 0; i < points.length; i++) {
			hashedPoints[hashPoint(points[i])] = points[i];
		}
		return hashedPoints;
	}	
	function isPointLoaded(point) {
		if (loadedPoints[hashPoint(point)]) {
			return true;
		}
		return false;
	}
	return pub;
};
/**
 * TMAPY pouzivaji souradnicovy system s42 a ten je nutne
 * prevest na google map souradnicovy system coz je wgs84
 */
vyletnik.map.google.getLatLng = function (x, y) {
	return new GLatLng(x, y);
};
vyletnik.map.google.BaseMarkerCreater = function (infoWindowShower) {
	var pub = {
		createMarker : function (point) {
			var marker = vyletnik.map.google.createMarker(point, new GSize(24, 24));
			GEvent.addListener(marker, 'click', function () {
				infoWindowShower.show(point);
			});
			return marker;		
		}
	};
	return pub;
};
vyletnik.map.google.createHighLightMarker = function (point) {
	return vyletnik.map.google.createMarker(point, new GSize(30, 30), 9999);
};
vyletnik.map.google.createMarker = function (point, size, z) {
	tIcon = new GIcon(G_DEFAULT_ICON);
	tIcon.image = point.mapIcon;
	tIcon.iconSize = size;
	tIcon.shadow = null;
	var options = {icon : tIcon};
	//implementace z-indexu
	if (z) {
		options.zIndexProcess = function () {
			return z;
		};
	}
	return new GMarker(vyletnik.map.google.getLatLng(point.x, point.y), options);	
};
