var Searcher = (function(){
	google.load('search', '1.0');
	var queryCashe = {};
	jQuery(function(){
		jQuery('.by-google').each(function(){
			google.search.Search.getBranding(this);
		});
	})
	return function(query, found, notFound, limit, resultSize){
		(!isset(limit))&&(limit = 10);
		(!isset(resultSize))&&(resultSize = 'large');
		if (isset(queryCashe[query])&&isset(queryCashe[query][limit])&&isset(queryCashe[query][limit][resultSize])){
			(queryCashe[query][limit][resultSize]) ? (found(queryCashe[query][limit][resultSize])) : (notFound());
		}else{
			var searcher 	= new google.search.WebSearch();
			//searcher.setRestriction(google.search.Search.RESTRICT_EXTENDED_ARGS,{ 
			//	'gl': 'UA',
			//	'lr': 'lang_ru'
			//});
			var results 	= [];
			var currentPageIndex = -1;
			searcher.setNoHtmlGeneration();
			searcher.setResultSetSize(resultSize);
			searcher.setSearchCompleteCallback(
				this,
				function(){
					if ((searcher.results.length)&&(searcher.cursor.currentPageIndex>currentPageIndex)){
						currentPageIndex = searcher.cursor.currentPageIndex;
						each(searcher.results, function(){
							this.resultNumber = results.length;
							results.push(this);
						});
						if ((searcher.cursor.currentPageIndex >= (limit-1))||(searcher.cursor.currentPageIndex == (searcher.cursor.pages.length-1))){
							(!isset(queryCashe[query]))&&(queryCashe[query] = {});
							(!isset(queryCashe[query][limit]))&&(queryCashe[query][limit] = {});
							queryCashe[query][limit][resultSize] = results;
							found(results);
						}else{
							searcher.gotoPage(searcher.cursor.currentPageIndex+1);
						}
					}else{
						queryCashe[query] = false;
						notFound();
					}
				}
			);
			searcher.execute(query);
		}
	}
})();
var Search;
var Place;
Event.bind('load.search', function(){
	Search = (function(){
		var firstSearch = true;
		//ServerByStreetSearch
			var initServerByStreetSearch = function(query, finished){
				var self = this;
				self.resultByUrlIndex = {};
				self.query 				= query;
				self.threads 			= 0;
				self.allThreads 	= 0;
				self.doneThreads 	= 0;
				self.finished 		= function(){
					firstSearch 		= false;
					finished();
				};
				self.getQuery(function(data){
					self.searchId = data.searchId;
					Status.set('searchGoogle');
					each(data.querys, function(queryKey, query){
						self.searchThread(query, queryKey, data.limit, data.pageSize);
					});
				});
			}
			initServerByStreetSearch.prototype = {
				startThread: function(){
					var self = this;
					self.threads++;
					self.allThreads++;
				},
				endThread: function(){
					var self = this;
					self.threads--;
					self.doneThreads++;
					Status.set('threadDone', {
						all: self.allThreads,
						done: self.doneThreads
					});
					if (self.threads == 0){
						self.finished();
					}
				},
				addResults: function(results, queryKey, resultsId){
					(isset(resultsId))||(resultsId=false);
					var self = this;
					for (var key in results){
					//each(results, function(key){
						var result = results[key];
						if (!self.resultByUrlIndex[queryKey][result.url]){
							continue
						}
						ResultPoint.add({
							point: {
								house: result.house,
								lat: result.lat,
								lng: result.lng,
								rel: (result.accuracy.slice(0,1)),
								title: result.street
							},
							resultId: [key,resultsId],
							query: self.query,
							relevance: result.relevance,
							result: self.resultByUrlIndex[queryKey][result.url]
						});
					}
					//});
					Event.notify('search.threadFinished');
				},
				replaceCharacters: function(string){
					return (string
						.replace(RegExp('\\<b\\>[\\.]{4}\\<\\/b\\>', 'gi'), 'longbr')
						.replace(RegExp('\\<b\\>[\\.]{1,3}\\<\\/b\\>', 'gi'), 'shortbr')
						.replace(RegExp('\\<[^\\>]{1,}\\>', 'gi'), ' ')
						.replace(RegExp('[^a-z^A-Z^а-я^А-Я^\\d^\\-^\\/^\]{1,}', 'gi'), ' ')
						.replace(RegExp('[\\s]{2,}', 'gi'), ' '));
				},
				getUploadResult: function(result, queryKey){
					var self = this;
					var returnResult = [];
					self.resultByUrlIndex[queryKey] = {}
					//each(result, function(){
					for (var key=0;key<result.length;key++){
						var resultItem = result[key];
						self.resultByUrlIndex[queryKey][resultItem.unescapedUrl] = resultItem;
						returnResult.push({
							u: resultItem.unescapedUrl,
							t: self.replaceCharacters(resultItem.titleNoFormatting),
							c: self.replaceCharacters(resultItem.content)
						})
					}
					//});
					return returnResult;
				},
				getResults: function(result, queryKey){
					var self = this;
					ServerAction({
						method: 'maplos/get_results',
						data: {
							search_id: self.searchId,
							query_key: queryKey,
							result: serialize(self.getUploadResult(result, queryKey))
						},
						postMethod: 'post',
						handler: {
							success: function(data){
								self.addResults(data.results, queryKey, data.resultsId);
								self.endThread();
							},
							notFound: function(){
								self.endThread();
							},
							error: function(){
								self.endThread();
							}
						}
					});
				},
				searchThread: function(query, queryKey, limit, pageSize){
					var self = this;
					self.startThread();
					Searcher(query, function(result){
						self.getResults(result, queryKey);
					}, function(){
						self.endThread();
					}, limit, pageSize);
				},
				getQuery: function(handler){
					var self 			= this;
					var bounds 		= Map.getBounds();
					var position = 'none';
					if (!firstSearch){
						var position 	= GPoint.toServer(Map.lat)
							+':'+GPoint.toServer(Map.lng)
							+':'+Map.zoom
							+':'+Map.layer;
					}
					ServerAction({
						method: 'maplos/get_query',
						data: {
							position: position,
							query: self.query,
							lat_min: bounds.lat.min,
							lat_min: bounds.lat.min,
							lat_max: bounds.lat.max,
							lng_min: bounds.lng.min,
							lng_max: bounds.lng.max
						},
						handler: {
							success: function(data){
								handler(data);
							},
							error: function(){
								self.finished();
							}
						}
					})
				}
			}
		//ServerGlobalSearch
			var initServerGlobalSearch = function(query, finished){
				var self 							= this;
				self.finished = function(){
					firstSearch = false;
					finished();
				};
				self.resultByUrlIndex = {};
				self.query 						= query;
				Status.set('searchGoogle');
				Searcher(Pattern('search.global.searchPattern',{
					query: self.query
				}), function(result){
					Status.set('resolveResults');
					self.handleResult(result);
				}, function(){
					self.finished();
				});
			}
			initServerGlobalSearch.prototype = {
				handleResult: function(result){
					var self = this;
					ServerAction({
						method: 'maplos/global_search',
						data: {
							query: self.query,
							result: serialize(self.getUploadResult(result, 0))
						},
						postMethod: 'post',
						handler: {
							success: function(data){
								self.addResults(data.results, 0, data.resultsId);
								self.finished();
							},
							notFound: function(){
								self.finished();
							},
							error: function(){
								self.finished();
							}
						}
					});
				}
			}
			AddMethods(initServerGlobalSearch, {
				replaceCharacters: initServerByStreetSearch.prototype.replaceCharacters,
				getUploadResult: initServerByStreetSearch.prototype.getUploadResult,
				addResults: initServerByStreetSearch.prototype.addResults
			});
		//GlobalPlaceSearch
			var initGlobalPlaceSearch = function(query, finished, type){
				var self 							= this;
				self.type = (isset(type)) ? (type) : ('good');
				self.goodPoints = [];
				self.finished = function(result){
					if (result){
						result.goodPoints = self.goodPoints;
					}
					finished(result)
				};
				self.resultByUrlIndex = {};
				self.query 						= query;
				Status.set('searchGoogleForPlace');
				Searcher(Pattern('search.global.searchPattern',{
					query: self.query
				}), function(result){
					Status.set('resolveResults');
					self.handleResult(result);
				}, function(){
					self.finished(false);
				});
			}
			initGlobalPlaceSearch.prototype = {
				defineRegion: function(results){
					var self = this;
					var resultPoints = {}
					each(results, function(){
						var id = (''+this.lat)+(''+this.lng);
						if (!isset(resultPoints[id])){
							resultPoints[id] = {
								lng: 		this.lng,
								lat: 		this.lat,
								house: 	this.house,
								street: this.street
							};
							resultPoints[id].relevanceAr = [];
						}
						resultPoints[id].relevanceAr.push({
							consider: (this.accuracy=='exactly')?(true):(false),
							relevance: this.relevance
						})
					});
					var maxRelevance 			= 0;
					var maxRelevancePoint = false;
					var averageRelevance 	= 0;
					var countForAverage 	= 0;
					for (var id in resultPoints){
						resultPoints[id].relevance = ResultPoint.countRelevance(
							resultPoints[id].relevanceAr
						);
						if(resultPoints[id].relevance > maxRelevance){
							maxRelevance = resultPoints[id].relevance;
							maxRelevancePoint = resultPoints[id];
						}
						(resultPoints[id].relevance >1)&&(self.goodPoints.push(resultPoints[id]));
						averageRelevance+=resultPoints[id].relevance;
						countForAverage++;
					}
					averageRelevance = averageRelevance/countForAverage;
					if ((maxRelevance > 4)&&(maxRelevance/averageRelevance >4)){
						return self.finished(maxRelevancePoint);
					}
					var relevantResults = [];
					for (var id in resultPoints){
						if (resultPoints[id].relevance >=1){
							relevantResults.push(resultPoints[id]);
						}
					}
					var radius = [3000, 2000, 1000];
					var distance;
					var maxInRadius = [7,5,3];
					var nearestPoints = [[],[],[]];
					for (var id1=0; id1<relevantResults.length;id1++){
						relevantResults[id1].inRadius = [-1, -1, -1];
						for (var id2=0; id2<relevantResults.length;id2++){
							distance = (
								 Math.pow(relevantResults[id1].lat-relevantResults[id2].lat, 2)
								+Math.pow(relevantResults[id1].lng-relevantResults[id2].lng, 2)
							);
							for (var key=0; key<radius.length;key++){
								if(distance<(radius[key]*radius[key])){
									relevantResults[id1].inRadius[key]++;
								}else{
									break;
								}
							}
						}
						for (var key=0; key<radius.length;key++){
							if (relevantResults[id1].inRadius[key]>maxInRadius[key]){
								maxInRadius = relevantResults[id1].inRadius;
								nearestPoints[key] = [relevantResults[id1]];
							}else if(relevantResults[id1].inRadius[key]==maxInRadius[key]){
								nearestPoints[key].push(relevantResults[id1]);
							}
						}
					}
					var nearestPoint = false;
					for (var key=0; key<nearestPoints.length;key++){
						if(nearestPoints[key].length>0){
							nearestPoint = nearestPoints[key][0];
						}
					}
					if ((!nearestPoint)&&(maxRelevance>1)&&(self.type=='any')){
						nearestPoint = maxRelevancePoint;
					}
					self.finished(nearestPoint);
				},
				handleResult: function(result){
					var self = this;
					ServerAction({
						method: 'maplos/global_search',
						data: {
							query: self.query,
							result: serialize(self.getUploadResult(result, 0))
						},
						postMethod: 'post',
						handler: {
							success: function(data){
								self.defineRegion(data.results);
							},
							notFound: function(){
								self.finished(false);
							},
							error: function(){
								self.finished(false);
							}
						}
					});
				}
			}
			AddMethods(initGlobalPlaceSearch, {
				replaceCharacters: initServerGlobalSearch.prototype.replaceCharacters,
				getUploadResult: initServerGlobalSearch.prototype.getUploadResult
			});
		var defineSearch = function(query, finished){
			if (Map.zoom == 12){
				new initServerGlobalSearch(query, finished);
			}else if (Map.zoom >= 13){
				new initServerByStreetSearch(query, finished);
			}
		}
		return {
			placeSearch: function(query, finished){
				new initGlobalPlaceSearch(query, finished);
			},
			init: function(query, finished){
				defineSearch(query, finished);
			}
		}
	})();
	Place = (function(){
		var initPlace = function(){
			var self = this;
		}
		initPlace.prototype = {
			search: function(query, success, unsuccess){
				var self = this;
				new initLocalSearch(query, function(result){
				//new initGeosearch(query, function(result){
					new initPlaceResult(result, function(){
						success();
					});
				}, function(){
					unsuccess();
				});
			}
		}
		var initLocalSearch = function(query, found, notFound){
			var self = this;
			if (trim(query)==Env.get('where.query')){
				var result = Env.get('where.result');
				if (result=='notFound'){
					notFound();
				}else{
					found({
						house: 	result.house,
						title: 	result.title,
						kind:		result.kind,
						lat: 		result.lat,
						lng:		result.lng
					});
				}
			}else{
				ServerAction({
					method: 'maplos/place_search',
					data: {
						query: query
					}, 
					handler: {
						success: function(data){
							found({
								house: data.house,
								title: data.title,
								kind: data.kind,
								lat: data.lat,
								lng: data.lng
							})
						},
						notFound: function(){
							notFound();
						},
						error: function(){
							notFound();
						}
					}
				});
			}
		}
		var initGeosearch = function(query, found, notFound){
			var self = this;
			self.prefix = Pattern('main.searchPrefix');
			var searcher =  new YMaps.Geocoder(self.prefix+query, {results:1});
			YMaps.Events.observe(searcher, searcher.Events.Load, function(){
				if (
					(this.length())
					&&(inArray(this.get(0).kind,['house', 'street', 'metro', 'route', 'bridge', 'cemetery', 'vegetation']))
				){
					var result = this.get(0);
					found({
						house: self.getGeocoderResultHouse(result),
						title: self.getGeocoderResultTitle(result),
						kind: result.kind,
						lat: GPoint.toServer(result.getGeoPoint().getLat()),
						lng: GPoint.toServer(result.getGeoPoint().getLng())
					});
				}else{
					notFound();
				}
			})
		}
		initGeosearch.prototype = {
			getGeocoderResultHouse: function(result){
				if (result.kind == 'house'){
					return result.AddressDetails.Country.Locality.Thoroughfare.Premise.PremiseNumber;
				}
				return false;
			},
			getGeocoderResultTitle: function(result){
				var title = false;
				if (isset(result.AddressDetails.Country.Locality.Thoroughfare)){
					title = result.AddressDetails.Country.Locality.Thoroughfare.ThoroughfareName;
				}else if (isset(result.AddressDetails.Country.Locality.Premise)){
					title = result.AddressDetails.Country.Locality.Premise.PremiseName;
				}
				return title;
			}
		}
		var initPlaceResult = function(result, handler){
			var self 				= this;
			self.result 		= result;
			self.resultItem = jQuery(Pattern(
				'result.place',
				{
					adress: self.resultTitle()
				}
			));
			(!self.result.house)&&(self.showStreetOnMap());
			if (inArray(self.result.kind, ['street', 'house'])){
				self.addStreetInfo(function(){
					Results.addResult(self.resultItem);
					handler();
				});
			}else{
				Results.addResult(self.resultItem);
				handler();
			}
		}
		initPlaceResult.prototype = {
			showStreetOnMap: function(){
				var self = this;
				Map.showAdressLabel({
					title: self.resultTitle(),
					lat: self.result.lat,
					lng: self.result.lng
				})
			},
			resultTitle: function(){
				var self = this;
				var title = self.result.title;
				if (inArray(self.result.kind, ['house', 'street'])){
					title = title.replace(RegExp(Pattern('result.placeTitleReplace'), 'gi'),'');
				}
				return title;
			},
			showHouses: function(data){
				var self = this;
				var streetHouses = jQuery(Pattern('result.placeStreetHouses', {
					street: self.streetTitle
				}));
				self.resultItem.append(streetHouses);
				var houseHolder = self.resultItem.find('.houses');
				each(data.houses, function(){
					var houseAr = this;
					var houseItem = jQuery(Pattern('result.placeStreetHouse', {
						house: houseAr.house
					})).find('.action').click(function(){
						self.showHouseOnMap(houseAr, jQuery(this))
					}).end();
					houseItem.appendTo(houseHolder);
					if (self.result.house && (self.result.house==houseAr['house'])){
						houseItem.find('.action').click();
					}
				});
			},
			showHouseOnMap: function(houseAr, element){
				var self = this;
				if (self.selectedHouse){
					self.selectedHouse.removeClass('select')
				}
				element.addClass('select');
				self.selectedHouse = element;
				Map.showAdressLabel({
					title: self.streetTitle+' '+houseAr['house'],
					lat: houseAr['lat'],
					lng: houseAr['lng']
				});
				SearchForm.input.value(Pattern('main.adressSearchPattern')+self.result.title+' '+houseAr['house'], 'silent');
			},
			addStreetInfo: function(handler){
				var self 		= this;
				self.selectedHouse 	= false;
				self.streetTitle 		= self.resultTitle();
				ServerAction({
					method: 'maplos/street_info',
					data: {
						street: self.result.title
					},
					handler: {
						success: function(data){
							self.showHouses(data);
							handler();
						},
						error: function(){
							handler();
						}
					}
				});
				
			}
		}
		return new initPlace();
	})();
})
