(function($){
	$.fn.isMapSearchForm = function(mapCanvasElement, options) {
		if (this.size()==0) { return; }
		if (!options) {
			options = {};
		}
		var _mapCanvasElement = $(mapCanvasElement);
		var _mapCanvas = _mapCanvasElement.data('class');
		var _root = this;
		var _action;
		var _map = _mapCanvas.getMap();
		var _init = function() {
			_root.submit(function(event){
				_class.overallSearch();
				return false;
			});
			_action = _root.attr('action');
			google.maps.event.addListenerOnce(_mapCanvas.getMap(),
					'tilesloaded', _class.overallSearch);
			_mapCanvas.bind('bounds_changed', _class.boundsChanged);
			_mapCanvas.bind('map_clicked', _class.removeAllRestaurantBubble);
		};
		var _class = {
			// Properties
			currentRestaurantId: null,
			nextBoundsChangeTime: 0,
			restaurantHash: {},

			// Methods
			overallSearch: function(){
				_class.restaurantSearch();
				_class.geoSearch();
				return false;
			},
			restaurantSearch: function() {
				if ($('#map_search_box').val() != $('#map_search').data('query')) {
					$('#restaurant_search_result').find('li').remove();
					$('#map_search').data('query', $('#map_search_box').val());
					_class.clearRestaurants();
					_class.removeAllRestaurantBubble();
				}
				_class.boundsSearch();
				return false;
			},
			boundsChanged: function() {
				var now = new Date();
				var nowInMilliseconds = now.getTime();
				//if (_class.lastBoundsChangeTime == null || nowInMilliseconds - _class.lastBoundsChangeTime > 2000) {
				_class.nextBoundsChangeTime = nowInMilliseconds + 1000 - 10;
				if (nowInMilliseconds > _class.nextBoundsChangeTime) {
					_class.boundsSearch();
				} else {
					setTimeout(_class.boundsChangeDelegate, 1000);
				}
				//}
			},
			boundsChangeDelegate: function() {
				var now = new Date();
				var nowInMilliseconds = now.getTime();
				if (nowInMilliseconds > _class.nextBoundsChangeTime) {
					_class.boundsSearch();
				}
			},
			boundsSearch: function() {
				if (_action == 'javascript:void(0)') {
					return false;
				}
				if (typeof(options.beforeBoundsSearch) == 'function') {
					if (options.beforeBoundsSearch() === false) {
						_class.clearRestaurants();
						return false;
					}
				}
				try {
					var bounds = _mapCanvas.getBounds().join(',');
					var center = _mapCanvas.getCenter().join(',');
					var withoutChain = $('#map_without_chain_filter:checked').val();
					$.get(_action, {'bounds':bounds, 'center':center, 'withoutChain': withoutChain,
						'q': $('#map_search').data('query'), 'cityName':$('#map_area_search_city').val(),
						'event':$('#map_search').data('event')},
						_class.boundsSearchCallback);
				} catch(e) {}
			},
			boundsSearchCallback: function(restaurantList, resultType) {
				var bounds = _map.getBounds();
				if (resultType == 'search' || resultType == 'search_dish') {
					_class.clearRestaurants();
				} else {
					if ($('#map_canvas_left').hasClass('disabled')) {
						_class.clearRestaurants();
						return;
					}
					for (var restaurantId in _class.restaurantHash) {
						var latLng = _mapCanvas.getPinPosition('restaurant_' + restaurantId);
						if (!_mapCanvas.boundsContains(latLng[0], latLng[1])) {
							_class.removeRestaurant(restaurantId);
						}
					}
				}
				for (var i = 0; i < restaurantList.length; i++) {
					_class.putRestaurant(restaurantList[i]);
				}
				if (typeof(options.afterBoundsSearchCallback) == 'function') {
					options.afterBoundsSearchCallback(restaurantList);
				}
			},
			removeRestaurant: function(restaurantId) {
				var pinId = 'restaurant_' + restaurantId;
				_mapCanvas.removePin(pinId);
				$('#restaurant_'+ restaurantId).remove();
				$('#restaurant_sneak_' + restaurantId).remove();
				$('#restaurant_sneak.restaurant_sneak' + restaurantId).html('');
				_mapCanvas.removeCircle(pinId);
				_mapCanvas.removeBubble(pinId);
				delete _class.restaurantHash[restaurantId];
				if (typeof(options.afterRemoveRestaurant) == 'function') {
					options.afterRemoveRestaurant(restaurantId);
				}
			},
			putRestaurant: function(restaurantObject) {
				var restaurantId = restaurantObject['id'];
				if (_class.restaurantHash[restaurantId]) {
					return;
				}
				var pinId = 'restaurant_' + restaurantId;
				var latitude = restaurantObject['latLng'][0];
				var longitude = restaurantObject['latLng'][1];
				var restaurantName = restaurantObject['name'];
				var restaurantUuid = restaurantObject['uuid'];
				var restaurantSearchResult = $('#restaurant_search_result');
				var li = $('#restaurant_' + restaurantObject['id']);
				if (li.size()==0 || restaurantSearchResult.size() > 0) {
					li = $('<li id="restaurant_' + restaurantObject['id'] + '" class="restaurant'
							+ restaurantObject['id']
							+ (restaurantObject['added']
							? ' added added_' + restaurantObject['id'] + ''
							: '')
							+ (restaurantObject['closed'] ? ' closed' : '')
							+ '"><a class="triggerAjax restaurants_item" id="restaurant_bubble_trigger_'
							+ restaurantId
							+ '" href="restaurant_clicked.php?uuid=' + restaurantUuid + '">' //点击了餐厅的链接在这里！
							+ ((restaurantName.length > 18)
							? (restaurantName).substr(0,17) + '...'
							: restaurantName)
							+'</a></li>').appendTo(restaurantSearchResult);
					li.find('a').bind('click', _class.restaurantListClickClosure(restaurantId));
					//li.isSneak();
				} else if (restaurantSearchResult.size() > 0) {
					li.removeAttr('rel').isSneak();
				}
				var style = restaurantObject['closed'] ? _mapCanvas.RED_DOT_TINY
						: restaurantObject['rating'] == 3 ? _mapCanvas.RED_STAR
						: restaurantObject['rating'] == 2 ? _mapCanvas.RED_PIN
						: _mapCanvas.RED_DOT_TINY;
				var zIndex = restaurantObject['rating'] == 3 ? 1 : 0;
				var pin = _mapCanvas.pin(pinId, {
					latitude: latitude,
					longitude: longitude,
					title: restaurantName,
					style: style,
					zIndex:zIndex,
					onClick: _class.restaurantPinClickClosure(restaurantId)
				});
				_class.restaurantHash[restaurantId] = restaurantObject;
				if (typeof(options.afterPutRestaurant) == 'function') {
					options.afterPutRestaurant(restaurantId);
				}
			},
			placeSearch: function(query) {
				var map = _mapCanvas.getMap();
				var request = {
					location: map.getCenter(),
					radius: '3000',
					name: query
				};
				var geoSearchResultClosure = function(query) {
					var locality = $('#map_area_search_city').val();
					return function(response) {
						_class.geoSearchResult(locality, query, response);
					}
				}
				var service = new google.maps.places.PlacesService(map);
				service.search(request, geoSearchResultClosure(query));
				return;
			},
			geoSearch: function() {
				var locality = $('#map_area_search_city').val();
				if (!locality.match(/市$/)) {
					locality += '市';
				}
				var searchBox = $('#map_area_search_box');
				var query = searchBox.val();
				query = $.trim(query).replace(locality, '');
				if (query == '') {
					_mapCanvas.removePin('geocode_result');
					return false;
				}
				if (locality + ' ' + query == searchBox.data('last_query')) {
					return false;
				}
				/*
				if (query.indexOf('p:') == 0) {
					query = query.replace('p:', '');
					_class.placeSearch(query);
					return false;
				}
				*/

				if (query.match(/^\d+\.\d+\,\s*\d+\.\d+$/)) {
					var latLngArray = query.split(/\,\s*/);
					var latLng = new google.maps.LatLng(latLngArray[0], latLngArray[1]);
					_class.geoSearchResult(locality, query, [{formatted_address: latLngArray.join(','), geometry:{location: latLng}}]);
					return;
				}
				_mapCanvas.geocode(locality, query, _class.geoSearchResult);
				searchBox.data('last_query', locality + ' ' + query);
				return false;
			},
			geoSearchResult: function(city, query, response, status) {
				if (!response) {
					return;
				}
				$('#map_place_selector').remove();
				if (response.length == 0) {
					_mapCanvas.toast('很抱歉，地图没能找到您的街道地址。');
				} else if (response.length == 1) {
					var geometry = response[0].geometry;
					var title = response[0].formatted_address || response[0].name;
					if (title == '中国北京市' || title == "中国北京") {
						_mapCanvas.toast('很抱歉，地图没能找到您的街道地址。');
						return;
					}
					var latitude = geometry.location.lat();
					var longitude = geometry.location.lng();
					_mapCanvas.panTo(latitude, longitude);
					_mapCanvas.pin('geocode_result', {
						latitude: latitude,
						longitude: longitude,
						title: title,
						style: _mapCanvas.BLUE_PIN
					});
					_mapCanvas.boundsChanged();
					if (typeof(options.afterGeoSearch) == 'function') {
						options.afterGeoSearch(city, query, response, status);
					}
				} else {
					_class.showPlaceSelector(response);
				}
			},
			showPlaceSelector: function(response) {
				var selector = $('#map_place_selector');
				if (selector.size()==0) {
					selector = $('<div id="map_place_selector">'
							+ '<h3>你要找的是哪个?</h3><ul></ul></div>')
							.insertAfter(_mapCanvasElement);
				}
				var ul = selector.find('ul').first();
				ul.html('');
				for (var i = 0; i < response.length; i++) {
					var address = response[i].formatted_address || response[i].name;
					var li = $('<li>'+address+'</li>').appendTo(ul);
					li.one('click', _class.selectAPlace(response[i]));
				}
			},
			selectAPlace: function(place) {
				return function() {
					_class.geoSearchResult('北京市',
							$('#map_area_search_box').val(), [place], 200)
				}
			},
			restaurantPinClickClosure: function(restaurantId) {
				return function() {
					$('#restaurant_bubble_trigger_' + restaurantId).click();
					if (typeof(options.onRestaurantPinClick) == 'function') {
						options.onRestaurantPinClick(restaurantId);
					}
					try {
						$('#restaurant').scrollTo($('#restaurant_' + restaurantId), 500);
					} catch(e) {}
				}
			},
			restaurantListClickClosure: function(restaurantId) {
				return function() {
					if (typeof(options.onRestaurantClick) == 'function') {
						options.onRestaurantClick(restaurantId);
					}
					return false;
				}
			},
			clearRestaurants: function() {
				_class.removeAllRestaurantBubble();
				for (var restaurantId in _class.restaurantHash) {
					_mapCanvas.removePin('restaurant_' + restaurantId);
					delete _class.restaurantHash[restaurantId];
					if (typeof(options.afterRemoveRestaurant) == 'function') {
						options.afterRemoveRestaurant(restaurantId);
					}
				}
				$('#restaurant_search_result').html('');
			},
			removeAllRestaurantBubble: function() {
				if (_class.currentRestaurantId == null) {
					return;
				}
				var pinId = 'restaurant_' + _class.currentRestaurantId;
				var restaurantObject = _class.restaurantHash[_class.currentRestaurantId];
				var style = restaurantObject['closed'] ? _mapCanvas.RED_DOT_TINY
						: restaurantObject['rating'] == 3 ? _mapCanvas.RED_STAR
						: restaurantObject['rating'] == 2 ? _mapCanvas.RED_PIN
						: _mapCanvas.RED_DOT_TINY;
				var zIndex = restaurantObject['rating'] == 3 ? 1 : 0;
				_mapCanvas.removeCircle(pinId);
				_mapCanvas.removeBubble(pinId);
				_mapCanvas.pin(pinId, {
					style:style,
					zIndex:zIndex
				});
				for (circleId in _mapCanvas.circleHash) {
					_mapCanvas.removeCircle(circleId);
				}
				$('#restaurant_' + _class.currentRestaurantId).removeClass('active');
				_class.currentRestaurantId = null;
			}
		};
		_init();
		_root.data('class', _class);
		return _class;
	};
})(jQuery);
