var tow = tow || {};
tow.map = tow.map || {};

tow.map.Map = function (opts)
{
	this._limit = null;
	var map = null;
	var clusterer = null;
	var $map = null;
	this._ready = false;
	this._title = null;
	var markers = [];
	var markerZIndex = 0;
	this.icons = {};

	this._containers = {};
	this._$postContainer = null;
	this.pageTitleId = '';
	this.pageDescriptionId = '';
	this.contentId = null;

	var imageFolder = '/assets/images_static/maps/';
	var map_bounds_changed_skip = 0;
	var map_bounds_changed_timer = 0;
	var self = this;
	var options = {};
	this.defaults = {};

	this.init = function (opts)
	{
		options = $.extend(options, this.defaults, opts);
		$map = $('#'+options.map_id);
		if ($map) {
			var size = options.size && new google.maps.Size(options.size[0], options.size[1]) || null;
			var center = options.center && new google.maps.LatLng(options.center[0], options.center[1]) || new google.maps.LatLng(10, 0);

			map = new google.maps.Map($map.get(0), $.extend({
				'center': center,
				'zoom': 1,
				'mapTypeId': google.maps.MapTypeId.ROADMAP,
				'mapTypeControl': false,
				'scrollwheel' : false
			}, options.map));
			this.mapLoadedHandler(options.use_tiles);

			var HelperOverlay = function (map) { this.setMap(map); }
			HelperOverlay.prototype = new google.maps.OverlayView();
			HelperOverlay.prototype.onAdd = function () {};
			HelperOverlay.prototype.onRemove = function () {};
			HelperOverlay.prototype.draw = function () {};
			this._helperOverlay = new HelperOverlay(map);

			this._ready = true;

			this.bind();
		}
	};

	this.bind = function ()
	{
		google.maps.event.addListener(map, 'bounds_changed', this.map_bounds_changed_handler);
		google.maps.event.addListener(map, 'tilesloaded', this.map_tilesloaded_handler);
	};

	this.show = function ()
	{
		$map.css('visibility', 'visible');
	};

	this.add_feed = function (feed, type) {
		var html = '';
		var current_count = 0;

		var output = [];
		var len = markers.length;
		var $container = $('#'+options.map_id+'_'+type).html('');
		var markers_portion = [];

		$.each(feed, function (i, entry) {
			// Look for existing markers to avoid double adding
			var found = false;
			$.each(markers, function (j, marker) {
				if (marker.content_id == entry.id) {
					found = true;
					return false;
				}
			});
			if (found) {
				return;
			}

			// Create and add a marker to the array
			if (entry.location && entry.location.lat && entry.location.lng) {
				var marker = new google.maps.Marker({
					'position': new google.maps.LatLng(parseFloat(entry.location.lat), parseFloat(entry.location.lng)),
					'zIndex': ++markerZIndex,
					'icon': self.icons[type][0]
				});
				marker.content_id = entry.id;
				marker.title = entry.title;
				if (entry.group) {
					marker.title += '<br/><span class="group">(via the ' + entry.group + ' group)</span>';
				}
				marker.link = entry.link;
				marker.type = type;
				marker.date = entry.date;
				marker.meta = entry.location.meta;
				marker.zoom = entry.location.zoom;
				marker.summary = entry.summary;

				if (options.show_lines && entry.location.meta && entry.location.meta.line) {
					var points = new google.maps.MVCArray();
					$.each(entry.location.meta.line, function (index, vtx) {
						points.push(new google.maps.LatLng(vtx.lat, vtx.lng));
					});
					var line = new google.maps.Polyline({
						'map': map,
						'path': points
					});
				}

				markers_portion.push(marker);
				if (self._marker_add_handler) {
					self._marker_add_handler(marker);
				}

				google.maps.event.addListener(marker, 'mouseover', self.marker_mouseover_handler);
				google.maps.event.addListener(marker, 'mouseout', self.marker_mouseout_handler);
				google.maps.event.addListener(marker, 'click', self.marker_click_handler);
				google.maps.event.addListener(marker, 'dblclick', self.marker_dblclick_handler);

				// Highlight current marker
				if (entry.active) {
					google.maps.event.trigger(marker, 'mouseover');
				}

				len++;
				current_count++;
			}
		});

		if (clusterer) {
			clusterer.addMarkers(markers_portion);
		} else {
			for (var i=0; i<markers_portion.length; i++) {
				markers_portion[i].setMap(map);
			}
		}
		markers = markers.concat(markers_portion);

		$(output).appendTo($container);

		$container.find('a.title').bind('mouseover', function (event) {
			var id = parseInt($(event.target).attr('id').replace(/.*_/, ''));
			if (markers[id]) {
				map.panTo(markers[id].getPosition());
				map.setZoom(markers[id].zoom);
				google.maps.event.trigger(markers[id], 'mouseover');

				if (self.pageTitleId && self.pageDescriptionId) {
					$('#'+self.pageTitleId).html(markers[id].title);
					if (markers[id].content_id == self.contentId) {
						var $desc = $('#'+self.pageDescriptionId+'_'+markers[id].content_id);
					} else {
						var $desc = $('#'+self.pageDescriptionId);
						$('#'+self.pageDescriptionId+'_link').attr('href', markers[id].link);
					}
					$('.'+self.pageDescriptionId).not($desc).hide();
					$desc.show();
				}
			}
		}).bind('mouseout', function (event) {
			var id = parseInt($(event.target).attr('id').split('_')[1]);
			if (markers[id]) {
				google.maps.event.trigger(markers[id], 'mouseout');
			}
		});

		return current_count;
	}

	this.clear = function () {
		if (clusterer) {
			clusterer.clearMarkers();
		} else {
			for (var i=0; i<markers.length; i++) {
				if (markers[i]) {
					markers[i].setMap(null);
				}
			}
		}
		markers = [];
	};

	this.setContainerId = function (containerId)
	{
		this._$userContainer = $('#'+containerId);
	};

	// Handlers

	this.hideTitle = function (speed)
	{
		if (!speed) {
			speed = 'slow';
		}
		$map.find('#title').fadeOut(speed);
	};

	this.set_center = function (location)
	{
		map.setCenter(new google.maps.LatLng(location.lat, location.lng));
	};

	this.set_zoom = function (zoom)
	{
		map.setZoom(zoom);
	};

	this.get_bounds = function ()
	{
		return map.getBounds();
	};

	this.choose_marker = function (marker_id)
	{
		$.each(markers, function (index, marker) {
			if (!marker) {
				return;
			}
			if (marker.content_id == marker_id) {
				map.panTo(marker.getPosition());
				map.setZoom(marker.zoom);
				google.maps.event.trigger(marker, 'mouseover');
				return false;
			}
		});
	};

	this.create_clusterer = function (Clusterer)
	{
		clusterer = new Clusterer(map, null, {
			'maxZoom': 14,
			'zoomOnClick': false
		});
		google.maps.event.addListener(clusterer, 'clusterclick', this.clusterer_clusterclick_handler);
		return clusterer;
	};

	this.get_clusterer_size = function ()
	{
		if (clusterer) {
			return clusterer.getMarkers().length;
		} else {
			return null;
		}
	};

	this.get_viewport_markers_count = function ()
	{
		var bounds = this.get_bounds();
		var count = 0;
		if (clusterer) {
			$.each(clusterer.getClusters(), function (index, cluster) {
				if (bounds.intersects(cluster.getBounds())) {
					count += cluster.getSize();
				}
			});
		} else {
			$.each(markers, function (index, marker) {
				if (bounds.contains(marker.getPosition())) {
					count++;
				}
			});
		}
		return count;
	};

	this.fit_markers = function ()
	{
		var bounds = new google.maps.LatLngBounds();
		$.each(markers, function (index, marker) {
			bounds.extend(marker.getPosition());
		});
		if (!bounds.isEmpty()) {
			map.fitBounds(bounds);
		}
	};

	this.setMarkerPopupHandler = function (handler) {
		this._marker_popup_handler = handler;
	};

	this.setMarkerAddHandler = function (handler) {
		this._marker_add_handler = handler;
	};

	this.map_bounds_changed_handler_body = function ()
	{
		clearTimeout(map_bounds_changed_timer);
		$(self).trigger('bounds_changed.map.tow', [map.getBounds()]);
	};

	this.map_bounds_changed_handler = function ()
	{
		/*
		if (map_bounds_changed_skip > 0) {
			// Skip bounds_changed trigger once
			map_bounds_changed_skip--;
			return;
		}
		*/
		if (map_bounds_changed_timer) {
			clearTimeout(map_bounds_changed_timer);
		}
		map_bounds_changed_timer = setTimeout(self.map_bounds_changed_handler_body, 500);
	};

	this.map_tilesloaded_handler = function ()
	{
		$(self).trigger('tiles_loaded');
	};

	this.marker_mouseover_handler = function (event)
	{
		var marker = this;
		marker.setZIndex(++markerZIndex);
		marker.setIcon(self.icons[marker.type][1]);
		setTimeout(function () {
			marker.setIcon(self.icons[marker.type][0]);
		}, 1000);
		if (event) {
			self._$title.attr('href', marker.link);
			self._$title.html('<div>' + marker.title + '</div>');
			self._$title.addClass(marker.type);

			if (self._marker_popup_handler) {
				self._marker_popup_handler(marker, self._$title);
			}

			var point = self._helperOverlay.getProjection().fromLatLngToContainerPixel(marker.getPosition());
			self._$title.css({left: point.x + 'px', top: point.y + 'px'});
			self._$title.css({left: (point.x - self._$title.width() * 0.5) + 'px', top: point.y + 'px'});

			self._$title.fadeIn('fast');
		}
	};

	this.marker_mouseout_handler = function (event)
	{
		var marker = this;
		marker.setIcon(self.icons[marker.type][0]);
	};

	this.marker_click_handler = function (event)
	{
		var marker = this;
		$(self).trigger('click.marker.map.tow', [marker]);
	};

	this.marker_dblclick_handler = function ()
	{
		var marker = this;
		location = marker.link;
	}

	this.mapLoadedHandler = function (useTiles)
	{
		$map.append('<a id="title"/>');
		self._$title = $map.find('#title').css({
			position: 'absolute',
			background: '#fff',
			display: 'block',
			zIndex: '9999'
		}).hide();

		self._$title.mousemove(function () {
			if (self.hideTitle.handle) {
				window.clearTimeout(self.hideTitle.handle);
			}
		}).mouseout(function () {
			self.hideTitle.handle = window.setTimeout(self.hideTitle, 2000);
		});

		// Event handlers
		google.maps.event.addListener(map, 'dragstart', self.map_dragstart_handler);

		if (useTiles) {
			// Create the tile layer overlay and
			// implement the three abstract methods
			var tilelayer = new google.maps.ImageMapType({
				'alt': 'TOW map',
				'normalizeTile': function (tile, count) {
					// tile x and y can be any numbers so we need to normalize it to tiles count
					var obj = {};
					$.extend(obj, tile);
					obj.x = obj.x % count;
					obj.y = obj.y % count;
					if (obj.x < 0) { obj.x += count }
					if (obj.y < 0) { obj.y += count }
					return obj;
				},
				'getTileUrl': function (tile, zoom) {
					tile = this.normalizeTile(tile, Math.pow(2, zoom));
					switch (zoom) {
					case 0:
						return cdn+"/assets/images_static/maps/zoom.1.gif";
					case 1:
						return "/assets/images_static/maps/zoom.2." + tile.x + "." + tile.y + ".gif";
					case 2:
						return "/assets/images_static/maps/zoom.3/zoom.3." + tile.x + "." + tile.y + ".jpg";
					default:
						return null;
					}
				},
				'isPng': function () {
					return false;
				},
				'maxZoom': 2,
				'minZoom': 0,
				'name': 'TOW',
				'opacity': 1,
				'tileSize': new google.maps.Size(256, 256)
			});
			map.mapTypes.set('TOW', tilelayer);
			map.setMapTypeId('TOW');
		}

		this.show();
	};

	this.map_dragstart_handler = function ()
	{
		self.hideTitle('fast');
	};

	this.clusterer_clusterclick_handler = function (cluster)
	{
		/*
		// No need to reload items, skip bounds_changed trigger 2 times
		// because clusterer triggers this event twice
		self._skip_bounds_changed_trigger = 2;
		*/

		var bounds = new google.maps.LatLngBounds(cluster.getCenter(), cluster.getCenter());
		$.each(cluster.getMarkers(), function (index, marker) {
			bounds.extend(marker.getPosition());
		});
		map.fitBounds(bounds);
	};

	// Constructor call
	this.init(opts);

};


tow.map.HomepageBlock = function (map_id)
{
	this.map = null;
	this.id = null;
	var self = this;

	this.init = function (id)
	{
		this.id = id;
	};

	this.set_map = function (map)
	{
		this.map = map;
	};

	this.set_items = function (items, type)
	{
		var $template = $('#'+this.id+'_'+type+'_template').children();
		var $container = $('#'+this.id+'_'+type).html('');
		var output = [];
		$.each(items, function (index, entry) {
			if (typeof(entry.location.meta) != 'undefined') {
				var country = entry.location.meta.country.toLowerCase();
				if (country === 'usa') { country = 'us'; }
				output.push(
					$template.clone()
						.find('a.title')
						.text(entry.title)
						.attr('id', entry.id)
						.attr('href', entry.link)
						.bind('mouseover', self.item_link_mouseover_handler)
						.end()
						.find('img.icon_flag')
						.attr('src', entry.location.meta.country && cdn+'/assets/images_static/icons/flags/'+country+'.png' || '')
						.end()
						.find('.date')
						.text(entry.date || '')
						.end()
				);
			}
		});
		$(output).each(function (index, entry) {
			$container.append(entry);
		});
	};

	this.item_link_mouseover_handler = function (event)
	{
		self.map.choose_marker($(this).attr('id'));
	};

	this.init(map_id);

};


