/*
 * myMap, custom Google Map v0.1, jQuery plugin
 *
 * Copyright(c) 2011, Dario Marcato
 * dm0388@gmail.com
 *
 * Load a Google Maps with custom markers.
 * 
 * Usage:
 * $('#div').myMap(data, options);				Load a Google map in the container, data can be a string (url to fetch data from) or a json object with markers data, options is an object with map options
 * $('#div').myMap('centerMap', where);			Where can be a string (address to lookup) or an object (coordinates): {latitude: xxx, longitude: yyy}
 * $('#div').myMap('loadMarkers', data);		Load markers from data on a pre-initialized map, data can be a string or an object
 * $('#div').myMap('remove');					Remove the Google map from the container
 */
(function($) {

    var internal = {
		zoomLevel: 12,									//Initial zoom level
		center: null,									//Where to center the map at startup, can be a string (address), an object (coordinates) {latitude: xxx, longitude: yyy} or null to try to get user position from the client
		defaultCenter: 'Italy',							//Default map center if 'center' is not found, same type as 'center'
		mapType: google.maps.MapTypeId.ROADMAP,			//Map type at startup
		streetView: true,								//Streetview enabled
		iconSize: 25,									//Marker icon size
		centerOnMarkers: true,							//If markers are available, center the map to show them all
		markerBalloon: false,							//Tell if show a balloon when clicking on markers or go directly at the url
		disableSearchBox: false,						//Tell if show the search box for filtering markers
		searchBoxClass: 'myMapSearchBox',				//SearchBox CSS class
		searchBoxUrl: '',								//Script url from where to fetch data, default to the one passed at init
		searchBoxSubmitText: 'Cerca',					//Text to show on the submit button
		searchBoxFilterMarkers: true,					//Tell if the search box filter the markers displayed or only move on the map
		
		/***** INTERNAL USE, DO NOT MODIFY! *****/
		container: null,
		theMap: null,
		markers: [],
    	infoMarkers: [],
		lastSearch: ''
    };
    
    var methods = {
        init: function(data, options) {
	        if ($(this).data('myMap')) {
        		methods.remove();
        	}
        	internal.container = $(this);
        	internal = $.extend(internal, options);
            
			var mapsOptions = {
				zoom: internal.zoomLevel,
				mapTypeId: internal.mapType,
				streetViewControl: internal.streetView
			};
            internal.theMap = new google.maps.Map(internal.container[0], mapsOptions);
			
			internal.container.data('myMap', 'loaded');
	        
	        if (data) {
	        	methods.loadMarkers(data);
	        } else {
	        	methods.centerMap(internal.center);
	        }
            
            return this;
        },
		_showSearchBox: function() {
			$('.'+internal.searchBoxClass).remove();
			var searchBox = $(document.createElement('div')).addClass(internal.searchBoxClass).insertBefore(internal.container);
			var searchForm = $(document.createElement('form')).attr({
				action: internal.searchBoxUrl,
				method: 'get'
			}).appendTo(searchBox);
			var searchField = $(document.createElement('input')).attr({
				type: 'text',
				name: 'search'
			}).appendTo(searchForm);
			var searchSubmit = $(document.createElement('input')).attr({
				type: 'submit',
				value: internal.searchBoxSubmitText
			}).appendTo(searchForm).click(function(e) {
				e.preventDefault();
				var params = "";
				if (location.search == "") {
					params = '?'+searchForm.serialize();
				} else {
					params += '&'+searchForm.serialize();
				}
				internal.lastSearch = searchForm.find('input[name=search]').val();
				if (internal.searchBoxFilterMarkers) {
					methods.loadMarkers(searchForm.attr('action')+params);
				} else {
					methods.centerMap(internal.lastSearch);
				}
			});
		},
        centerMap: function(where) {
        	if (where == null || where == '') {
	            methods._getUserPosition();
	        } else if (typeof where === 'string') {
	        	methods._centerFromAddress(where);
			} else if (typeof where === 'object') {
				internal.theMap.setCenter(new google.maps.LatLng(where.latitude, where.longitude));
	        } else {
	        	methods._centerDefault();
	        }
	        internal.theMap.setZoom(internal.zoomLevel);
	        return this;
        },
        _loadMapScript: function() {
        	var script = document.createElement("script");
			script.type = "text/javascript";
			script.src = "http://maps.google.com/maps/api/js?sensor=true&callback=null";
			document.body.appendChild(script);
        },
        _getUserPosition: function() {
			// Try W3C Geolocation (Preferred)
        	if(navigator.geolocation) {
				navigator.geolocation.getCurrentPosition(function(position) {
					initialLocation = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
					internal.theMap.setCenter(initialLocation);
				}, function() {
					methods._centerMapDefault();
				});
			// Try Google Gears Geolocation
			} else if (google.gears) {
				var geo = google.gears.factory.create('beta.geolocation');
				geo.getCurrentPosition(function(position) {
					initialLocation = new google.maps.LatLng(position.latitude,position.longitude);
					internal.theMap.setCenter(initialLocation);
				}, function() {
					methods._centerMapDefault();
				});
			// Browser doesn't support Geolocation
			} else {
				methods._centerMapDefault();
			}
        },
        _centerMapDefault: function() {
        	if (internal.defaultCenter == null || internal.defaultCenter == '') {
	        	internal.theMap.setCenter(new google.maps.LatLng(-34.397, 150.644));
	        } else if (typeof internal.defaultCenter === 'string') {
	        	methods._centerFromAddress(internal.defaultCenter);
	        } else if (typeof internal.defaultCenter === 'object') {
	        	internal.theMap.setCenter(new google.maps.LatLng(internal.defaultCenter.latitude, internal.defaultCenter.longitude));
	        }
        },
        _centerFromAddress: function(address) {
        	methods._getLatLngFromAddress(address, '_centerFromCoord');
        },
        _centerFromCoord: function(coord) {
       		internal.theMap.setCenter(coord);
        },
        _getLatLngFromAddress: function(address, callback) {
        	var geocoder = new google.maps.Geocoder();
        	var args = arguments;
		    geocoder.geocode({'address': address}, function(results, status) {
				if (status == google.maps.GeocoderStatus.OK) {
					methods[callback](results[0].geometry.location, Array.prototype.slice.call(args, 2));
				} else {
					//alert('Address not found: '+address);
					methods[callback](null, Array.prototype.slice.call(args, 2));
				}
			});
        },
        loadMarkers: function(data) {
			if (internal.container == null || !internal.container.data('myMap')) {
        		alert('myMap not loaded!');
        		return;
        	}
        	methods._clearMarkers();
        	if (typeof data === 'object') {
            	methods._processMarkers(data);
            } else if (typeof data === 'string') {
				if (internal.searchBoxUrl == '') {
					internal.searchBoxUrl = data;
				}
				if (!internal.disableSearchBox) {
					methods._showSearchBox();
				}
	
                $.ajax({
                    type: 'GET',
                    dataType: 'jsonp',
                    url: data,
                    error: function() {
	                    methods.centerMap(internal.lastSearch);
                    	alert('Error while fetching markers data');
                    },
                    success: function(data) {
                    	if (typeof data === 'object') {
							methods._processMarkers(data);
						} else {
							alert('Invalid data returned');
						}
                    }
                });
            } else {
           		methods.centerMap(internal.lastSearch);
            }
            return this;
        },
        _processMarkers: function(data) {
			var risCount = 0;
			for (var k in data) {
			    if (data.hasOwnProperty(k)) {
			       ++risCount;
			    }
			}
        	if (risCount == 0) {
				if (internal.lastSearch != '') {
					methods.centerMap(internal.lastSearch);
				} else {
	        		methods.centerMap(internal.center);	
				}
        	} else {
				for (var key in data) {
					if (data.hasOwnProperty(key)) {
						if (typeof data[key].geo_address === 'string') {
							methods._getLatLngFromAddress(data[key].geo_address, '_createMarker', data[key]);
						} else {
							methods._createMarker(new google.maps.LatLng(data[key].geo_address.latitude, data[key].geo_address.longitude), [data[key]]);
						}
					}
				}
			}
        },
        _createMarker: function(coord, data) {
			if (coord != null) {
	        	data = data[0];
	        	var mIcon = (data.geo_image) ? new google.maps.MarkerImage(data.geo_image, new google.maps.Size(internal.iconSize, internal.iconSize)) : null;
	        	var marker = new google.maps.Marker({
					position: coord,
					map: internal.theMap,
					title: data.geo_description,
					icon: mIcon
				});
				var infowindow = new google.maps.InfoWindow({
					content: data.geo_description_link
				});
				google.maps.event.addListener(marker, 'click', function() {
					if (internal.markerBalloon) {
						infowindow.open(internal.theMap, marker);
					} else {
						location.href = data.geo_link;
					}
				});
				internal.markers.push(marker);
				internal.infoMarkers.push(infowindow);
			
				if (internal.centerOnMarkers) {
					var latlngbounds = new google.maps.LatLngBounds();
					for (i in internal.markers) {
						latlngbounds.extend(internal.markers[i].getPosition());
					}
					internal.theMap.fitBounds(latlngbounds);
				}
			}
        },
        _clearMarkers: function() {
        	for (i in internal.markers) {
        		internal.markers[i].setMap(null);
        	}
        	internal.markers.length = 0;
        	internal.infoMarkers.length = 0;
        },
        remove: function() {
        	if (internal.container == null || !internal.container.data('myMap')) {
        		alert('myMap not loaded!');
        		return;
        	}
        	internal.theMap = null;
        	internal.container.removeData('myMap');
        	internal.container.html('');
        	internal.container.attr('style', '');
        	internal.container = null;
        	return this;
        }
    };

    $.fn.myMap = function(method) {
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'string' || typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method ' + method + ' does not exists on jQuery.myMap');
        }
    };
})(jQuery);
