/**
 * Mappr v1.0
 *
 * Author: Matthew Kingston <matthew@clickcreative.com.au>
 *         Copyright: 2011 © Click Creative Pty Ltd
 * 
 */
(function(window, $, undefined)
{
	
	
	/**
	 * Creates a new Mappr instance and also attaches
	 * it to the global scope to access it via window.Mappr
	 */
	var Mappr = window.Mappr = {
		
		/**
		 * The current version of Mappr.
		 *
		 * @public
		 * @type String
		 */
		version: "1.0",
		
		/**
		 * An array of map elements and options
		 *
		 * @private
		 * @type Array
		 */
		maps: [],
		
		/**
		 * A service for converting between an address and a LatLng.
		 *
		 * @private
		 * @type google.maps.Geocoder Object
		 */
		geocoder: null,
		
		/**
		 * Whether the google maps API has been loaded and
		 * furthermore called the initialization method
		 *
		 * @private
		 * @type Bool
		 */
		initialized: false,
		
		/**
		 * The country for which to set as the region of the (user)
		 * performing the search - gives better results.
		 */
		default_region: 'AU'
	}
	
	
	/**
	 * Initializes the Mappr class and lets any future calls to the create
	 * map method that the google API has been loaded and is ready.
	 *
	 * @public initialize
	 * @return void
	 */
	Mappr.initialize = function()
	{
		// Flag the mappr class as initialized which lets
		// us know that the google maps API has been successfully loaded.
		Mappr.initialized = true;
		
		// Create a new Geocoder instance
		Mappr.geocoder = new google.maps.Geocoder();
		
		// Iterate over existing elements and create a map for each of them
		$(Mappr.maps).each(function(i)
		{
			Mappr.createMap.apply(Mappr.maps[i].element, [Mappr.maps[i].options]);
		});
		
		Mappr.maps = [];
	}
	
	
	
	Mappr.createMap = function(options)
	{
		return this.each(function()
		{
			var self = $(this);
			var selfElement = self[0];
			
			// Check to see if the google API has been successfully loaded
			if(!Mappr.initialized)
			{
				// Add a new map element to the map array to be generated upon
				// Mappr initialization
				Mappr.maps.push({ 'element': self, 'options': options });
			}
			else
			{
				var locations = self.find('a');
				
				var markers = [];
				var markerBounds = new google.maps.LatLngBounds();
				var map = new google.maps.Map(selfElement, {
					'streetViewControl': false,
					'panControl': true,
					'zoomControl': true,
					'mapTypeControl': false,
					'overviewMapControl': false,
					'mapTypeId': google.maps.MapTypeId.ROADMAP
				});
				
				
				self.data('map', map);
				self.data('markers', markers);
				self.data('markerBounds', markerBounds);
				self.data('locations', locations);
				
				locations.each(function()
				{
					var location = $(this);
					var address = location.data('address') || location.text();
					
					Mappr.geocoder.geocode({ 'address': address }, function(results, status)
					{
						if(results && status == google.maps.GeocoderStatus.OK)
						{
							markers.push(new google.maps.Marker({ 'position': results[0].geometry.location, 'map': self.data('map') }));
							markerBounds.extend(results[0].geometry.location);
							
							map.setCenter(markerBounds.getCenter());
							map.fitBounds(markerBounds);
							
							self.data('map', map);
							self.data('markers', markers);
							self.data('markerBounds', markerBounds);
							
							google.maps.event.trigger(map, 'resize');
							
							self.Mappr('refreshMap');
						}
						else
						{
							console.log("Geocode was not successful for the following reason: " + status);
						}
					});
				});
				
				setTimeout(function()
				{
					self.Mappr('refreshMap');
				}, 3000);
				
				var parentElements = self.parents().not('html');
				
				/**
				 * Every time the map is hidden from view (ie. it's display is set to none,
				 * or a parent element's display is set to none) the map goes buggy. We need
				 * to set a listener to a css property of the element and all it's parents
				 * which allow the callback to trigger a 'refresh' of the map element and
				 * reposition the map to fit all the markers currently visible.
				 */
				//self.add(parentElements).watch('display', function() { self.Mappr('refreshMap'); });
			}
		});
	}
	
	
	Mappr.refreshMap = function()
	{
		var self = $(this);
		var map = self.data('map');
		var markers = self.data('markers');
		var markerBounds = self.data('markerBounds');
		var locations = self.data('locations');
		
		google.maps.event.trigger(map, 'resize');
		
		map.setCenter(markerBounds.getCenter());
		map.fitBounds(markerBounds);
		
		if(markers.length == 1)
		{
			if($(locations[0]).data('zoom'))
			{
				map.setZoom($(locations[0]).data('zoom'));
			}
			else
			{
				map.setZoom(10);
			}
		}
	}
	
	
	
	/**
	 * jQuery element prototype function that allows any jQuery
	 * element collection to call this method using the following:
	 *
	 * <code>
	 *     $('div').Mappr();
	 * </code>
	 *
	 */
	$.fn.Mappr = function(method)
	{
		if(Mappr[method])
		{
			return Mappr[method].apply(this, Array.prototype.slice.call(arguments, 1));
		}
		else if(typeof method === 'object' || !method)
		{
			return Mappr.createMap.apply(this, arguments);
		}
		else
		{
			console.log('Method ' +  method + ' does not exist in the Mappr object');
		}
	}
	
	
	
	
	/**
	 * Once the document is ready, check for elements that have the
	 * mappr class and turn them into a google map element
	 */
	$(function()
	{
		// Check for .mappr elements
		if($('*[class~="mappr"]').length){$('*[class~="mappr"]').Mappr();}
		
		// Create a new script element and add it to the DOM. Once the google map
		// API is loaded and ready it will call the Mappr.initialize method
		var google_url = ('https:'==location.protocol?'https://':'http://') +
		'maps.googleapis.com/maps/api/js?sensor=false';
		google_url += '&region=' + (Mappr.default_region || 'AU');
		google_url += '&callback=' + (Mappr.initialize_method || 'Mappr.initialize');
		script_tag(google_url);
	});
	
	
})(window, jQuery);
