(function($)
{
	$.fn.MapComponent = function(options)
	{
		var defaultSettings =
		{
			'minZoom': 0,
			'maxZoom': 18,
			'zoom': 13,
			'rightClickBlock': true,
			'showNavigationPanel': true,
			'showLayerPanel': true,
			'layers':
			{
				'map':
				{
					'zIndex': 1,
					'layerURLs':
					[
							'http://map1.korkowo.pl/traffic/layer-map-new/',
							'http://map2.korkowo.pl/traffic/layer-map-new/',
							'http://map3.korkowo.pl/traffic/layer-map-new/'
					],
					'visible': true,
					'description': 'Mapa',
					'browserCacheDuration': 86400000,
					'fadeIn': 0,
					'showInLayerPanel': false
				}
			},
			'center':
			{
				'longitude': 16.917161,
				'latitude': 52.407498
			},
			'tileSize': 256
		};

		var settings = $.extend({}, defaultSettings, options);

		var $this = $(this);

		var mapContainer = $('<div>').attr('id', 'mapContainer').css('position', 'absolute').css('left', 0).css('top', 0);
		$this.append(mapContainer);

		var tiles = {};
		
		if(settings.rightClickBlock) // blokowanie klikniecia PPM
		{
			$(this).bind("contextmenu", function(e)
			{
				e.preventDefault();
			});
		}
		
		if(settings.showNavigationPanel) showNavigationPanel();
		if(settings.showLayerPanel) showLayerPanel();
		
		function onCenterChanged(e, location)
		{
			if (location.latitude && location.longitude)
			{
				location.x = settings.center.x = longitudeToX(location.longitude);
				location.y = settings.center.y = latitudeToY(location.latitude);
			}
			if (location.x && location.y)
			{
				settings.center.x = location.x;
				settings.center.y = location.y;
			}
		}
		$this.bind('onCenterChanged', onCenterChanged);

		function onZoomChanged(e, zoom)
		{
			settings.center.x = settings.center.x * Math.pow(2, (zoom - settings.zoom));
			settings.center.y = settings.center.y * Math.pow(2, (zoom - settings.zoom));
			settings.zoom = zoom;
		}
		$this.bind('onZoomChanged', onZoomChanged);

		$this.bind('click dblclick mousedown mouseover mouseup', function(e)
		{
			e.mapPosition =
			{
				x: e.pageX - $this.offset().left + settings.center.x - $this.width() / 2,
				y: e.pageY - $this.offset().top + settings.center.y - $this.height() / 2
			};
			return e;
		});
		
		function onMouseDown(e)
		{
			e.preventDefault();
			
			settings.dragging = {};

			settings.dragging.start =
			{
				x: e.pageX,
				y: e.pageY
			};

			settings.dragging.original =
			{
				x: settings.center.x,
				y: settings.center.y
			};

			$(document).bind("mousemove", onMouseMove);
			$(document).bind("mouseup", onMouseUp);
		}

		function onMouseMove(e)
		{
			e.preventDefault();
			settings.dragging.dragOffset =
			{
				x: e.pageX - settings.dragging.start.x,
				y: e.pageY - settings.dragging.start.y
			};

			var newcenter =
			{
				x: settings.dragging.original.x - settings.dragging.dragOffset.x,
				y: settings.dragging.original.y - settings.dragging.dragOffset.y
			};

			$this.trigger("onCenterChanged", newcenter);
		}

		function onMouseUp(e)
		{
			$(document).unbind("mousemove", onMouseMove);
			$(document).unbind("mouseup", onMouseUp);
		}
		$this.bind("mousedown", onMouseDown);

		function onDblClick(e)
		{
			settings.center.x = e.mapPosition.x;
			settings.center.y = e.mapPosition.y;
			$this.triggerHandler("zoomIn");
		}
		$this.bind("dblclick", onDblClick);

		function onMouseScroll(event)
		{
			event = $.event.fix(event || window.event);
			event.type = "mousewheel";
			event.preventDefault();

			var delta = 0;
			if (event.wheelDelta) delta = event.wheelDelta / 120;
			if (event.detail) delta = -event.detail / 3;

			var mousePos =
			{
				x: event.pageX - $this.offset().top,
				y: event.pageY - $this.offset().left
			};

			if (delta > 0) $this.triggerHandler('zoomIn', mousePos);
			if (delta < 0) $this.triggerHandler('zoomOut', mousePos);
		}
		$this.bind('mousewheel', onMouseScroll);
		$this.bind('DOMMouseScroll', onMouseScroll);
		$this.onmousewheel = onMouseScroll;

		function zoomIn()
		{
			if (settings.zoom < settings.maxZoom) return $this.triggerHandler('onZoomChanged', settings.zoom + 1);
		}
		$this.bind('zoomIn', zoomIn);

		function zoomOut()
		{
			if (settings.zoom > settings.minZoom) return $this.triggerHandler('onZoomChanged', settings.zoom - 1);
		}
		$this.bind('zoomOut', zoomOut);

		function addMarker(event, options)
		{			
			var markerId = 1;
			while($('#map_marker' + markerId).length == 1)
			{
				markerId++;
			}
			
			var marker_content = '';

			if(options.object.type == 'image')
			{
				marker_content = $("<img/>").attr("src", options.object.src);
			}
			else if(options.object.type == 'raw')
			{
				marker_content = options.object.content;
			}

			var marker = $('<div>').attr('id', 'map_marker' + markerId).addClass('map_marker map_marker_' + options.object.type).html(marker_content);
			$this.append(marker);

			if(options.object.title) marker.attr('title', options.object.title);
			if(options.object.customClass) marker.addClass(options.object.customClass);

			if(options.object.type != 'image') options.object.width = marker.width();
			if(options.object.type != 'image') options.object.height = marker.height();

			var position = getOffset(options.location, options.object);

			marker.css("left", position.x).css("top", position.y);

			$this.bind("onZoomChanged onCenterChanged", function()
			{
				position = getOffset(options.location, options.object);
				marker.css("left", position.x);
				marker.css("top", position.y);
			}); 
		}
		$this.bind('addMarker', addMarker);
		
		function updateLayer(layerName, centerTile)
		{
			var layerSettings = settings.layers[layerName];

			var imgTemplate = $("<img/>").addClass('mTile').css("width", settings.tileSize).css("height", settings.tileSize);

			var x, y;
			
			for (x = -1 * centerTile.leftCount; x <= centerTile.rightCount; x++)
			{
				for (y = -1 * centerTile.topCount; y <= centerTile.bottomCount; y++)
				{
					var tileXID = x + centerTile.x;
					var tileYID = y + centerTile.y;
					var tileID = layerName + "_" + settings.zoom + "_" + tileXID + "_" + tileYID;

					var left = x * settings.tileSize + centerTile.xTotalOffset;
					var top = y * settings.tileSize + centerTile.yTotalOffset;

					if (tiles[layerName][tileID] == undefined)
					{
						var layerURL = layerSettings.layerURLs.length == 1
							? layerSettings.layerURLs[0] : layerSettings.layerURLs[Math.floor(Math.random() * layerSettings.layerURLs.length)];
						var url = prepareTileURL(layerURL, tileXID, tileYID, layerSettings.browserCacheDuration);

						var img = imgTemplate.clone();
						img.css("display", "none").css("left", left).css("top", top).css("z-index", layerSettings.zIndex);
						img.attr("id", tileID).attr("src", url);

						img.load(function()
						{
							if (layerSettings.fadeIn != 0)
							{
								$(this).fadeIn(layerSettings.fadeIn);
							}
							else
							{
								$(this).show();
							}
						});

						tiles[layerName][tileID] =
						{
							'img': img,
							'visible': true
						};
						mapContainer.append(img);
					}
					else
					{
						left = x * settings.tileSize + centerTile.xTotalOffset;
						top = y * settings.tileSize + centerTile.yTotalOffset;
						
						tile = tiles[layerName][tileID];
						tile.img.css("left", left).css("top", top);
						tile.visible = true;
					}
				}
			}
		}

		function updateMap()
		{
			for (layerName in tiles)
			{
				for (tileID in tiles[layerName])
				{
					tiles[layerName][tileID].visible = false;
				}
			}

			var centerTile =
			{
				'y': Math.floor(settings.center.x / settings.tileSize),
				'x': Math.floor(settings.center.y / settings.tileSize)
			};

			centerTile.xTileOffset = settings.center.x - settings.tileSize * centerTile.x;
			centerTile.yTileOffset = settings.center.y - settings.tileSize * centerTile.y;

			centerTile.xTotalOffset = Math.floor($this.width() / 2 - centerTile.xTileOffset);
			centerTile.yTotalOffset = Math.floor($this.height() / 2 - centerTile.yTileOffset); 

			centerTile.leftCount = Math.ceil(($this.width() / 2 - centerTile.xTileOffset) / settings.tileSize);
			centerTile.rightCount = Math.ceil(($this.width() / 2 + centerTile.xTileOffset) / settings.tileSize) - 1;

			centerTile.topCount = Math.ceil(($this.height() / 2 - centerTile.yTileOffset) / settings.tileSize); 
			centerTile.bottomCount = Math.ceil(($this.height() / 2 + centerTile.yTileOffset) / settings.tileSize) - 1;

			for (layerName in settings.layers)
			{
				if (tiles[layerName] == undefined) tiles[layerName] = {};
				if (settings.layers[layerName].visible) updateLayer(layerName, centerTile);
			}

			for (layerName in tiles)
			{
				for (tileID in tiles[layerName])
				{
					if (!tiles[layerName][tileID].visible)
					{
						$('#' + tileID).remove();
						delete tiles[layerName][tileID];
					}
				}
			}
		}
		$this.bind('updateMap', updateMap);
		$this.bind('onCenterChanged', updateMap);
		$this.bind('onZoomChanged', updateMap);

		function showLayerPanel()
		{
			var panelHeight = 20;
			var panelTop = $this.height() - panelHeight;

			var div = $('<div class="panel layer-panel">').css("position", "absolute").css("z-index", "10")
					.css("top", panelTop + "px").css("left", "0px").css("background-color", "#FAFAFA").css(
							"border", "1px solid #AAA");

			var checkBoxTemplate = $('<input type="checkbox">');

			for (layerName in settings.layers)
			{
				if (settings.layers[layerName].showInLayerPanel)
				{
					var checkBox = checkBoxTemplate.clone().attr("value", layerName).attr("checked", settings.layers[layerName].visible);
					checkBox.bind("click", function(e)
					{
						var layerName = $(this).attr('value');
						var visible = $(this).is(':checked');
						settings.layers[layerName].visible = visible;
						$this.trigger('updateMap');
					});

					div.append(checkBox);
					div.append(settings.layers[layerName].description);
				}
			}
			$this.append(div);
		}
		
		function showNavigationPanel()
		{
			var div = $('<div class="panel navigation-panel">').css("position", "absolute").css(
					"z-index", "10").css("top", "20px").css("left", "0px").css(
					"background-color", "#FAFAFA").css("border", "1px solid #AAA");

			var a = $('<a class="navigation-link" href="#">').css("text-decoration", "none");

			var zoomin = a.clone().addClass("navigation-link-zoomin").html("+").bind("click dblclick", function(e)
			{
				$this.trigger("zoomIn");
				e.preventDefault();
				e.stopPropagation();
			});

			var zoomout = a.clone().addClass("navigation-link-zoomout").html("-").bind("click dblclick", function(e)
			{
				$this.trigger("zoomOut");
				e.preventDefault();
				e.stopPropagation();
			});

			div.append(zoomin).append($('<br />')).append(zoomout);

			$this.append(div);
		}
		
		$this.css("position", "relative").css("overflow", "hidden");

		$this.trigger('onCenterChanged', settings.center);
		
		function longitudeToX(longitiude)
		{
			longitiude = parseFloat(longitiude);
			var x = (longitiude + 180) / 360;
			return Math.floor(x * settings.tileSize * Math.pow(2, settings.zoom));
		}

		function latitudeToY(latitude)
		{
			var radians = latitude * Math.PI / 180;
			var y = (1 - Math.log(Math.tan(radians) + 1 / Math.cos(radians)) / Math.PI) / 2;
			return Math.floor(y * settings.tileSize * Math.pow(2, settings.zoom));
		}

		function xToLongitude(x)
		{
			return 360 * (((x / settings.tileSize) / Math.pow(2, settings.zoom)) - 0.5);
		}

		function yToLatitude(y)
		{
			y = 0.5 - ((y / settings.tileSize) / Math.pow(2, settings.zoom));
			return 90 - 360 * Math.atan(Math.exp(-y * 2 * Math.PI)) / Math.PI;
		}

		function prepareTileURL(layerURL, x, y, browserCacheDuration)
		{
			var currentTime = new Date().getTime();
			var guid = currentTime - currentTime % browserCacheDuration;

			zoom = Math.floor(settings.zoom);
			var url = layerURL + "" + zoom + "/" + x + "/" + y + ".png?guid=" + guid;

			return url;
		}

		function getOffset(location, object)
		{
			var xObjectOffset, yObjectOffset;

			switch (object.position.vertical)
			{
				case 'top':
					yObjectOffset = -1 * object.height;
					break;
				case 'center':
					yObjectOffset = -0.5 * object.height;
					break;
				case 'bottom':
					yObjectOffset = 0;
					break;
				default:
					yObjectOffset = 0;
					break;
			}

			switch (object.position.horizontal)
			{
				case 'left':
					xObjectOffset = -1 * object.width;
					break;
				case 'center':
					xObjectOffset = -0.5 * object.width;
					break;
				case 'right':
					xObjectOffset = 0;
					break;
				default:
					xObjectOffset = 0;
					break;
			}

			var ret =
			{
				x: longitudeToX(location.longitude) - (settings.center.x - Math.floor($this.width() / 2)) + xObjectOffset,
				y: latitudeToY(location.latitude) - (settings.center.y - Math.floor($this.height() / 2)) + yObjectOffset
			}; 
			return ret;				
		}
	};
})(jQuery);
