function mapsGeoAPI(consumer){
	this.mapConsumer    = consumer;
	this.styleNamespace = "megafon#";
	
	var self = this,
		baseStyle = new YMaps.Style(),
		iconTemplate = new YMaps.Template('<div class="placemark $[style.iconStyle.megaclass]"></div>'),
		groupIconTemplate = new YMaps.Template('<div class="placemark $[style.iconStyle.megaclass]">$[metaDataProperty.megacount|0]</div>'),
		groupBubbleLayout = function(){
			this._offsets = {bottom: 30, left: -20};
			
			this.element = $(['<div id="map-bubble"><a href="" class="close">',
								'<img src="/i/maps/close.png" alt=""></a><ul></ul>',
								'<img class="tail" src="/i/maps/tail.png" alt=""></div>'].join('')).css(this._offsets);
			this.close   = this.element.find("> a.close");
			this.content = this.element.find("ul");
			
			this.enableClose = function(callback){
				this.close.bind("click", function() {callback.apply(this, arguments);return false;}).show();
				return false;
			};
			
			this.disableClose = function(){
				this.close.unbind("click").hide();
			};
			
			this.onAddToParent = function (parentNode) {
				YMaps.jQuery(parentNode).append(this.element);
			};
			
			this.onRemoveFromParent = function () {
				this.element.remove();
			};
			
			this.setContent = function (content) {
				content.onAddToParent(this.content[0]);
			};
			
			this.update = function() {this.content.empty()}
			
			this.getOffset = function() {return new YMaps.Point(-this._offsets.left, -( this.content.height() + this._offsets.bottom ))}
			
			this.getSize = function () {return new YMaps.Size(this.content.width(),this.content.height())}
		},
		groupBubbleTemplate = new YMaps.LayoutTemplate(groupBubbleLayout),
		groupBubbleContentTemplate = new YMaps.Template("<li>$[description]</li>"),
		commonBubbleTemplate = [groupBubbleTemplate, groupBubbleContentTemplate], 
		singlePoint = new YMaps.Style(baseStyle),
		singlePointHh = new YMaps.Style(baseStyle),
		groupPointSmall = new YMaps.Style(baseStyle),
		groupPointMedium = new YMaps.Style(baseStyle),
		groupPointLarge  = new YMaps.Style(baseStyle);

		$.each({"single": [singlePoint, iconTemplate],
				"single-hh": [singlePointHh, iconTemplate],
				"g-small": [groupPointSmall, groupIconTemplate, commonBubbleTemplate], 
				"g-medium": [groupPointMedium, groupIconTemplate, commonBubbleTemplate], 
				"g-large": [groupPointLarge, groupIconTemplate, commonBubbleTemplate]}, function(name, style){
			
			style[0].iconStyle = new YMaps.IconStyle(style[1]);
			style[0].iconStyle.megaclass = name
			if(style[2]) {
				style[0].balloonStyle = new YMaps.BalloonStyle(style[2][0]);
				style[0].balloonContentStyle = new YMaps.BalloonContentStyle(style[2][1]);
			}
			YMaps.Styles.add(self.styleNamespace + name, style[0])
		});
		
		this.toBalloonContent = function(list) {return list.join('</li><li>')};
		this.setStyleForPlacemark = function(options, count, happyHours) {
			switch(true){
				case count >= 30:
					options.style = this.styleNamespace + 'g-large'
					break;
				case count >= 5:
					options.style = this.styleNamespace + 'g-medium'
					break;
				case count > 1:
					options.style = this.styleNamespace + 'g-small'
					break;
				default:
					options.style = this.styleNamespace + 'single' + ((happyHours) ? '-hh' : '');
					break;
			}
		};
}

mapsGeoAPI.prototype = {
	initMap: function(container, mapName){
		var map = new YMaps.Map(container);
		map.MegafonMapName = mapName;
		$(window).unload(function(){map.destructor()});
		
		return map
	},
	
	initPoint: function(x, y, extra){
		return new YMaps.GeoPoint(parseFloat(x), parseFloat(y));
	},
	
	initPlacemark: function(geoPoint, _options){
		_options = this._applyDefaultOptions(_options);
		return new YMaps.Placemark(geoPoint, _options);
	},
	
	_applyDefaultOptions: function(_options){
		_options = _options || {};
		$.each({style: "default#greenPoint"}, function(k,v){
			if(!(k in _options)){
				_options[k] = v;
			}
		});
		
		return _options;
	},
	
	toggleAllMarks: function(markers, toggle) {
		for (var i in markers) {
			markers[i][toggle]();
		}
	},
	
	mapSetCenter: function(map, mapCenter, mapZoom) {
		map.setCenter(mapCenter || this.mapConsumer.maps[map.MegafonMapName].center, mapZoom || this.mapConsumer.maps[map.MegafonMapName].zoom);
	},
	
	mapAddControls: function(map) {
		map.addControl(new YMaps.Zoom());
		map.addControl(new YMaps.TypeControl());
		map.addControl(new YMaps.ScaleLine());
	},

	mapSetZoom: function(map, zoom) {
		map.setZoom(zoom);
	},

	mapGetZoom: function(map) {
		return map.getZoom();
	},
	
	mapAddOverlay: function(placemark, map){
		map.addOverlay(placemark)
	},
	
	clearMap: function(map) {
		map.removeAllOverlays();
	},
	
	resetMap: function(map){
		this.mapSetCenter(map);
		this.clearMap(map);
	},
	
	updateMap: function(map){
		map.update()
	},
	
	refreshMap: function(map){
		map.redraw(false)
	},
	
	bindEvent: function(listenOn, event, callback, context){
		event = this._events(listenOn)[event];
		YMaps.Events.observe(listenOn, event, callback, context)
	},

	_events: function(listenOn){
		return {
			"click":    listenOn.Events.Click,
			"zoom_end": listenOn.Events.SmoothZoomEnd,
			"zoom_start": listenOn.Events.SmoothZoomStart,
			"update":   listenOn.Events.Update
		}
	}
}

var officesControl = function() {
	this.modalContent = null;
	this.mApi = new mapsGeoAPI(this);
	this.maps = {main: {}, modal: {}}
	
	with(Office.regionSpecData){
		this.maps.main.center  = this.mApi.initPoint(regionCoords[0], regionCoords[1]);
		this.maps.main.zoom    = parseInt(mainMapSize);
		
		this.maps.modal.center = this.maps.main.center;
		this.maps.modal.zoom   = parseInt(modalMapSize);
	}

	this.changingFilters = false;
	this.filtersStack = {};

	this.markers = [];

	this.labels = Office.labels;

	this.options = {
		oaction:  "",
		hour:     "",
		day:      "",
		city:     ""
	};

	this.mainMapTabClass      = 'map';
	this.officesLinkClass     = 'modal-office-link'
	this.groupPlacemarksLimit = 10;
}

officesControl.prototype = {
	init: function() {
		this.favouriteBrowser = (navigator.userAgent.indexOf("MSIE 6") !== -1);

		this.modalWindow = $('<div class="modal"></div>')[0];
		this.officesTabs = $('#offices-tabs')
		$(this.officesTabs).removeClass('noshow');
		this.selects = $('.filters select', this.officesTabs);
		this.metroTab = $('#data0')[0];
		this.listTab = $('#data2');
		this.metroMap = $("#metromap")[0];
		this.officesForm = $('#findoffices')[0];
		this.listTabTable = $("table tbody", this.listTab)[0];

		this.initMainMap();
		this.tabControl();

		this.fillTitles();

		this.fillData();
		
		this.bindDisplayModal(this);
		
		this.changeFilters();
	},
	
	officesBalloon: null,
	
	openOfficesBalloon: function(point, offices, params) {
		if (this.officesBalloon)
			$(this.officesBalloon).remove();
		
		var self = this;
		params = $.extend(params,{
			drawList: function(links) {
				var r = '';
				for (i in links)
					r += '<li>' + links[i] + '</li>';
				r = '<ul>' + r + '</ul>';
				return r;
			},
			draw: function(offices) {
				var lsmargin = this.leftShift
					? 'margin-left:-' + this.leftShift + 'px'
					: '';
				return '<div id="map-bubble" style="bottom:30px;left:-20xp;'+lsmargin+'">'
					+ '<a href="#tab0" class="close"><img src="/i/maps/close.png" alt=""></a>'
					+ this.drawList(self.makeLinksListFromOffices(offices))
					+ '<img class="tail" style="'+(lsmargin.replace(/:-/, ':'))+'" src="/i/maps/tail.png" alt=""></div>';
			}
		});

		var balloon = this.officesBalloon = document.createElement('div');
		balloon.id = 'PXOfficesBalloon';
		balloon.innerHTML = params.draw(offices);
		balloon.style.position = 'absolute';
		balloon.style.left = (parseInt(point.x || point[0]) - 24) + 'px';
		balloon.style.top = (parseInt(point.y || point[1]) + 10) + 'px';
		balloon.style.zIndex = 2000;

		self.metroTab.style.position = 'relative';
		self.metroTab.appendChild(balloon);

		return balloon;
	},
	
	dropOfficesBalloon: function(){
		$(this.officesBalloon).remove();
	},

	fillTitles: function() {
		this.titles = function() {
			var tmp = [];

			for (i in Office.data) {
				tmp.push({id: i, title: jQuery.trim(Office.data[i].title)});
			}

			return tmp;
		}();

		this.titles.sort(function(a, b) {
			a = a.title.toUpperCase()[0];
			b = b.title.toUpperCase()[0];

			if (a == b) {
				return 0;
			}

			var res;
			try {
				res = a < b ? -1 : 1;
			} catch(e) {
				res = 1;
			}
			return res;
		});
	},

	tabControl: function(map, position) {
		/* Tab switching */
		var tabsCont = document.getElementById('office-tabs'),

			tabs = [],
			selClass = ' selected',
			i, j, tab, current, firstTab, offices = this;

		if(!tabsCont || !tabsCont.childNodes || !tabsCont.childNodes.length){
			return;
		}
		
		for (j=0; j < tabsCont.childNodes.length; j++) {
			tab = tabsCont.childNodes.item(j);

			if((i = tab.getAttribute('tabindex')) == null){
				continue;
			}
			
			tabs[i] = {
				tab: tab,
				data: document.getElementById('data' + i)
			};

			if(!firstTab && tabs[i].data){
				firstTab = tabs[i].data
			}

			if (tab.className && tab.className.match(selClass)) {
				current = i;
			}

			/* tabs show */
			(function(i) {
				tabs[i].tab.onmousedown = function() {
					if (current !== i) {
						for (var el in tabs[i]) {
							if (tabs[i].hasOwnProperty(el)) {
								tabs[current][el].className = tabs[current][el].className.replace(selClass, '');
								tabs[i][el].className += selClass;
							}
						}
						current = i;
						window.location.hash = 'tab' + i;
						
						if($(tabs[current].tab).hasClass(offices.mainMapTabClass)) {
							offices.mApi.refreshMap(offices.mainMap);
						}
					}
					return false;
				};
			}(i));

			tab.onselectstart = function() {return false;};
			 /* Rounded corners */
			tab.innerHTML += '<div class="leftcorner"></div><div class="rightcorner"></div>';
		}
		
		if(firstTab){
			this._createModal(firstTab.parentNode.parentNode);
		}
		
		if(window.location.hash.match(/tab(\d)/) && tabs[RegExp.$1]){
			tabs[RegExp.$1].tab.onmousedown()
		}
	},

	addModalContent: function(office) {
		this.officeId = office.id;
		var html = []
		if(office.title != '') {
			html[html.length] = ['<div class="address"><span>',this.labels.address,': </span><strong>', office.title, '</strong></div>'].join('');
		}

		if(office.shop != '') {
			html[html.length] = ['<div class="address"><span>',this.labels.place,': </span>', '<strong>', office.shop, '</strong></div>'].join('');
		}

		if(office.works != '') {
			html[html.length] = ['<div class="hours"><span>',this.labels.works,'</span>', '<strong>', office.works, '</strong></div>'].join('');
		}
		
		html[html.length] = ['<div class="address"><span>','телефон','</span>', '<strong>', '8 800 333-05-00', '</strong></div>'].join('');

		var desc = '';
		var hhText = this._createHappyHoursText(office);
		if (office.mapdescription || hhText) {
			desc = ['<div class="special-info">', hhText, office.mapdescription, '<div class="sctop"><div class="sr"></div><div class="sl"></div></div><div class="scbot"><div class="sr"></div><div class="sl"></div></div></div>'].join('');
		}
		html[html.length] = ['<div class="link"><span><a class="feedback-link" href="/feedback/office_visitors/?office=', office.id, '">',this.labels.option,'</a></span>',
			desc,
		'</div>'].join('');

		this.modalContent.innerHTML = html.join('');
		html = [];

		$('body').addClass('open-modal');

		var coords = (office.coords && office.coords.length)? office.coords.split(":") : [];
		if (coords.length === 2 && !office.hide_modal_map) {
			this._toggleModalMap(true)
			this._initModalMap(office.id, coords);
		} else {
			this._toggleModalMap(false)
		}

		if (this.favouriteBrowser) {
			this.selects.css('visibility', 'hidden');
		}
		
		this.smsForm.style.display = 'none';
		$(this.smsForm).removeClass('error').removeClass('send');

		this.modalContent.parentNode.scrollIntoView(true);
	},

	_createHappyHoursText: function(office) {
		var resultText = [];

		var daylyText = function(array) {
			var prosorinoPinaka = [], keimeno = [];
			for (i=0;i<24;i++) {
				if ($.inArray(i.toString(), array) != -1) {
					if (i==0 || ($.inArray((i-1).toString(), array) == -1)) {
						prosorinoPinaka.push([i]);
					} else {
						prosorinoPinaka[prosorinoPinaka.length-1].push(i);
					}
				}
			}
			for (i=0;i<prosorinoPinaka.length;i++) {
				if (prosorinoPinaka[i].length == 1) {
					keimeno.push(prosorinoPinaka[i][0]);
				} else {
					keimeno.push(prosorinoPinaka[i][0] + '-' + prosorinoPinaka[i][prosorinoPinaka[i].length-1]);
				}
			}
			return keimeno.join(', ');
		}
		if (office.hh_monday.length) {
			resultText.push('понедельник &mdash; ' + daylyText(office.hh_monday) + ' часов');
		}
		if (office.hh_tuesday.length) {
			resultText.push('вторник &mdash; ' + daylyText(office.hh_tuesday) + ' часов');
		}
		if (office.hh_wednesday.length) {
			resultText.push('среда &mdash; ' + daylyText(office.hh_wednesday) + ' часов');
		}
		if (office.hh_thursday.length) {
			resultText.push('четверг &mdash; ' + daylyText(office.hh_thursday) + ' часов');
		}
		if (office.hh_friday.length) {
			resultText.push('пятница &mdash; ' + daylyText(office.hh_friday) + ' часов');
		}
		if (office.hh_saturday.length) {
			resultText.push('суббота &mdash; ' + daylyText(office.hh_saturday) + ' часов');
		}
		if (office.hh_sunday.length) {
			resultText.push('воскресенье &mdash; ' + daylyText(office.hh_sunday) + ' часов');
		}
		if (resultText.length) {
			return '<p>Время наименьшей загруженности офиса: ' + resultText.join(', ') + '</p>' ;
		} else {
			return '';
		}
	},

	_createModal: function(cont) {
		var offices = this,
			o, map, divObj, div, links, smsForm, a, img, select, input;

		cont.appendChild(this.modalWindow);
		this.modalWindow.innerHTML = '<div class="bg"></div><div class="bot"></div><a class="close"></a>';

		this.modalContent = $('<div class="content"></div>')[0];

		this.modalWindow.onclick = function(e) {
			var t = (e ? e.target : window.event.srcElement);

			if ('feedback-link' === t.className) {
				window.open(t.href, '_blank', 'width=800, height=800, resizable=yes, scrollbars=yes');

				return false;
			} else if ('close' === t.className) {
				$('body').removeClass('open-modal');
				$('#office_tracker').prev().children('input:submit').removeAttr('disabled').end().end().remove();
				if (offices.favouriteBrowser) {
					offices.selects.css('visibility', 'visible');
				}
			}
		};

		map = document.createElement('div');
		map.className = this.mainMapTabClass;

		divObj = document.createElement('div');
		divObj.className = "mapObj";

		div = document.createElement('div');
		div.className = "shape";
		divObj.appendChild(div);
		this.modalWindow.appendChild(this.modalContent);
		this.modalWindow.appendChild(map);

		this.modalContainer = document.createElement('div');
		divObj.appendChild(this.modalContainer);
		map.appendChild(divObj);

		links = document.createElement('div');
		links.className = "map-links";

		/* form */
		smsForm = document.createElement('form');
		smsForm.id = 'smsform';
		smsForm.action = "/offices.action?action=phone";
		smsForm.method = "post";
		smsForm.className = "simple";
		smsForm.style.display = "none";
		
		smsForm.appendChild($('<div class="message-send">' + this.labels.messagesend + '</div>')[0]);
		smsForm.appendChild($('<p class="error"></p>')[0]);
		
		var labels = this.labels;
		
		a = document.createElement('a');
		a.className = "send-map";
		a.innerHTML = this.labels.send;
		links.appendChild(a);


		a.onclick = function() {
			offices.getCaptcha(offices);
			if(offices.codeval && offices.captchaImg) {
				smsForm.className = "simple";
				smsForm.style.display = 'block';
				offices.idInput.value = offices.officeId;
				offices.codeval.value = "";
				offices.captchaImg.src = '/captcha/' + offices.captcha + '.jpg';
			} else {
				smsForm.style.display = 'block';

				offices.captchaImg = $('<img alt="[CAPTCHA]" src="/captcha/' + offices.captcha + '.jpg" />')[0]
				smsForm.appendChild(offices.captchaImg);

				offices.captchaKey = $('<input name="codekey" type="hidden" value="' + offices.captcha + '"/>')[0];
				smsForm.appendChild(offices.captchaKey);

				offices.idInput = $('<input name="id" type="hidden" value="' + offices.officeId + '" />')[0];
				smsForm.appendChild(offices.idInput);

				offices.codeval = $('<input name="codeval" type="text" value="" maxlength="4" class="textfield" />')[0];
				smsForm.appendChild(offices.codeval);

				smsForm.appendChild(document.createElement('br'));

				select = $('<select name="prefix" class="date"></select>')[0];
				$.getJSON("/offices.action?action=prefix", function(data) {
						$.each(data, function(i,item) {
							o =  $('<option value="' + item.value + '">' + item.show + '</option>')[0];
							select.appendChild(o);
						});
					}
				);

				smsForm.appendChild(select);

				input = $('<input type="text" name="phone" notice ="' + labels.notice + '"pattern="[0-9]{7}" maxlength="7" value="" class="textfield" />')[0];
				smsForm.appendChild(input);

				input = $('<input type="submit" value="' + labels.submit + '" class="submit" />')[0];
				smsForm.appendChild(input);
				this.submitSmsForm = input;
			}

			return false;
		};

		a = document.createElement('a');
		a.className = "print-map";
		a.innerHTML = this.labels.print;
		links.appendChild(a);

		a.onclick = function() {
			window.print();
			return false;
		};

		smsForm.onsubmit = function() {
			if (TypicalValidateForm(this, true)) {
				with (this) {
					$.get("/offices.action?action=phone",
						{
							id:      id.value,
							prefix:  prefix.options[prefix.selectedIndex].value,
							phone:   phone.value,
							codeval: codeval.value,
							codekey: codekey.value
						},

						function(data) {
							if (data === 'send') {
								$(".map-links form", offices.modalWindow).removeClass('error').addClass('send').after('<iframe id="office_tracker" width="1" height="1" frameborder="no" scrolling="no" src="/popups/office_tracker.html?office=' + Office.data[offices.officeId].pathname.replace('.html', '') + '" ></iframe>');
							} else {
								$(".map-links form", offices.modalWindow).find('p.error').html(data).end().addClass('error');
								input.removeAttribute('disabled');
								offices.getCaptcha(offices);
								offices.captchaImg.src = '/captcha/' + offices.captcha + '.jpg';
								offices.captchaKey.value = offices.captcha;
							}
						}
					);
				}
			}
			
			return false;
		};

		links.appendChild(smsForm);
	
		/* end form */

		this.modalContent.parentNode.appendChild(links);
		this.smsForm = smsForm;
	},

	getCaptcha: function(offices) {
		$.ajax({
			type: "POST",
			url: "/captcha/",
			data: ({action : 'get_code'}),
			async: false, 
			success: function(data){
				offices.captcha = data;
			}
		});

	},

	_initModalMap: function(id, modalCoords) {
		if (document.documentElement.scrollTop > 0) {
			this.modalWindow.scrollIntoView(true);
		}
		
		var modalPoint  = this.mApi.initPoint(modalCoords[0], modalCoords[1]),
			options = {hasBalloon: false},
			modalMarker;
		
		this.mApi.setStyleForPlacemark(options)
		modalMarker = this.mApi.initPlacemark(modalPoint, options);

		if(!this.map){
			this.map = this.mApi.initMap(this.modalContainer, "modal")
			this.mApi.mapAddControls(this.map)
		}
		
		this.mApi.resetMap(this.map)
		this.mApi.mapAddOverlay(modalMarker, this.map)
		this.mApi.mapSetCenter(this.map, modalPoint)
		this.mApi.refreshMap(this.map)
	},

	_toggleModalMap: function(state) {
		$(this.modalWindow).toggleClass("no-map", !state);
	},                                                   

	initMainMap: function() {
		this.mainMap = this.mApi.initMap(document.getElementById('officesmap'), "main");
		this.mApi.mapSetCenter(this.mainMap);
		this.mApi.mapAddControls(this.mainMap);
		this.mApi.bindEvent(this.mainMap, "zoom_start", function(){this.redrawGroupedMarkers()}, this);
		this.mApi.bindEvent(this.mainMap, "zoom_end", function(){this.redrawGroupedMarkers()}, this);
		this.mApi.bindEvent(this.mainMap, "update", function(){this.redrawGroupedMarkers();}, this);
	},

	fillData: function() {
		this.metros = Office.metros || []; // global
		this.data = Office.data || []; // global

		// table and map
		for (i in this.titles) {
			var id = this.titles[i].id;
			var of = this.data[id];

			this.addTR(this.listTabTable, of);
			this.addPoint(of);
		}

		// metro map
		var offices = this,
			self = this,
			metro = {},
			ofs = [];
		for (i in this.metros) {
			metro = this.metros[i];
			ofs = (function(){
				var result = [];
				for (i in metro.offices)
					result.push(offices.data[metro.offices[i]]);
				return result;
			})();
			this.addMetro(metro, ofs);
		}

		var maxWidth = $(this.metroMap).find('>img:first').width(),
			balloonWidth = 310; // px
		$(this.metroMap).click(function(e){
			var a = $(e.target),
				visibleOffices = a.data('visibleOffices'),
				officesData = [],
				metro = {};
			if (!visibleOffices || !visibleOffices.length) {
				self.dropOfficesBalloon();
				return false;
			}
			for (i in visibleOffices)
				officesData.push(self.data[visibleOffices[i]]);
			metro = self.metros[a.data('metroId')];
			
			var balloonParams = {};
			if (maxWidth < parseInt(metro.coords[0]) + balloonWidth) { //-a.position().left-(e.offsetX||0);
				balloonParams.leftShift = - maxWidth + parseInt(metro.coords[0]) + balloonWidth;
			}
			
			self.openOfficesBalloon(metro.coords, officesData, balloonParams);
		});
		
		$(this.metroTab).find('#map-bubble a.close').live('click',function(e){
			e.preventDefault();
			e.stopPropagation();
			self.dropOfficesBalloon();
		});

		if (Office.current && Office.data[Office.current]) {
			this.addModalContent(Office.data[Office.current]);
		}

		$("input.radiobutton[value='0']", this.officesForm).attr("checked", "checked");

		$(this.officesForm).change(function() {
			self.changeFilters()
		});

		this.officesRows = $("tr", this.listTabTable);
		this.mapLinks = $("a", this.metroMap);
	},

	changeFilters: function() {
		var form = this.officesForm;

		if(form.theme){
			form.theme.onchange = function() {
				form.oaction.disabled = false;
				form.oaction.options.length = 0;
				var theme = form.theme.value;
				form.oaction.options[form.oaction.options.length] = new Option('---', '', false, false);
				var actions = Office.oactions[theme];
				for(var i in actions) {
					var ac = actions[i];
					form.oaction.options[form.oaction.options.length] = new Option(ac.title, ac.id, false, false);
					if(form.oaction.options[form.oaction.options.length-1]) {
						form.oaction.options[form.oaction.options.length-1].innerHTML = ac.title;
					}
				}
			}
		}

		if(form.oaction) {
			this.options['oaction'] = form.oaction.value;
		}

		this.options['hour'] = form.hour.value;
		this.options['day'] = form.day.value;

		var inputs = form.getElementsByTagName('input'),
			a = [],
			b = [],
			i;

		for (i = 0; i < inputs.length; i++) {
			if (inputs[i].checked && inputs[i].name === 'city[]') {
				b[b.length] = inputs[i].value;
			}
		}
		this.options["city"] = b.join(",");

		if(!this.changingFilters) {
			var cache = this.filtersStack[this.serializeOptions(this.options)];

			if (!cache) {
				var offices = this;
				this.changingFilters = setTimeout(
					function() {
						offices.sendRequest();
					}, 1500);
			} else {
				this.insertData(cache);
			}
		}

		return true;
	},

	insertData: function(data) {
		this.changingFilters = false;

		var visible = {},
			markers = this.markers,
			filteredMarkers = [];

		$.each(data, function(i, office){
			visible[office] = true;

			if (markers[office]) {
				filteredMarkers.push(markers[office]);
			}
		});

		this.redrawGroupedMarkers(this.actualMarkers = filteredMarkers, true);
		this.showFiltered(this.officesRows, visible);
		this.showFiltered(this.mapLinks, visible);
	},

	showFiltered: function(all, filter) {
		// hide all
		all.hide();
		// and show visible
		all.filter(function() {
			var offices = $(this).data('offices') || [$(this).attr('rel')],
			    visibleOffices = [];
			for (i in offices)
				if (filter[offices[i]])
					visibleOffices.push(offices[i]);
			if (!visibleOffices.length)
				return false;
			$(this).data('visibleOffices', visibleOffices);
			return true;
		}).show();
	},

	zoom: 0,
	redrawGroupedMarkers: function(markersList, forgetAboutZoom){ //FIXME: move Yandex specific to mapsGeoAPI object
		var newZoom = this.mApi.mapGetZoom(this.mainMap);
		if (!forgetAboutZoom && this.zoom == newZoom)
			return;

		this.zoom = newZoom;
		this.mApi.clearMap(this.mainMap);

		if(!(markersList = (markersList || this.actualMarkers || [])).length) {
			return;
		}
		
		var bounds  = this.mainMap.getBounds(),
			pointLb = bounds.getLeftBottom(),
			grid    = [10, 10],
			gCollection = new YMaps.GeoObjectCollection(),
			sub_x = (bounds.getRightTop().getLng() - bounds.getLeftTop().getLng()),
			sub_y = (bounds.getLeftTop().getLat()  - bounds.getLeftBottom().getLat()),
			grad_x  = sub_x / grid[0],
			grad_y  = sub_y / grid[1],
			top    = bounds.getLeftTop().getLat(),
			left   = bounds.getLeftTop().getLng(),
			getGroup = function(x, y) {
				return Math.floor((x - left) / grad_x) + "," + Math.floor((top - y) / grad_y);
			},
			offices = this,
			pointsGroups = {},
			calcCentralPointOfGroup = function(group) {
				var sum = {x:0, y:0}, qty = 0;
				for (var i in group) {
					sum.x += parseFloat(group[i].coords.x);
					sum.y += parseFloat(group[i].coords.y);
					qty ++;
				}
				if (!qty) return null;
				sum.x /= qty;
				sum.y /= qty;
				return offices.mApi.initPoint(sum.x, sum.y);
			};

		$.each(markersList, function(i, marker) {
			var group  = getGroup(marker.coords.x, marker.coords.y);

			if(!pointsGroups[group]) {
				pointsGroups[group] = [];
			}
			
			marker.point = offices.mApi.initPoint(marker.coords.x, marker.coords.y);
			
			pointsGroups[group].push(marker);
		});

		$.each(pointsGroups, function(key, group){
			var firstPoint = group[0],
				mark, options = {};
			if(group.length > 1){ //TODO: refactoring need
				offices.mApi.setStyleForPlacemark(options, group.length);
				mark = offices.mApi.initPlacemark(calcCentralPointOfGroup(group), options);
				mark.metaDataProperty.megacount = group.length;
				mark.description = offices.mApi.toBalloonContent(offices.makeLinksListFrom(group));
			} else {
				options.hasBalloon = false;
				var hh = offices.officeIsHappyHoursNow(Office.data[firstPoint.id]);
				offices.mApi.setStyleForPlacemark(options, 1, hh);
				mark = offices.mApi.initPlacemark(firstPoint.point, options);
				offices.mApi.bindEvent(mark, "click", function() {
					this.addModalContent(Office.data[firstPoint.id]);
				}, offices);
			}
			
			gCollection.add(mark);
		})

		this.mApi.mapAddOverlay(gCollection, this.mainMap)
	},

	officeIsHappyHoursNow: function(office) {
		if (this.officesForm.day.value == '') {
			return false;
		} else {
			if (this.officesForm.hour.value == '') {
				if ($.inArray(this.officesForm.day.value, office.hh_weekdays) != -1) {
					return true;
				}
			} else {
				var days = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];
				if ($.inArray(this.officesForm.hour.value, office['hh_' + days[this.officesForm.day.value-1]]) != -1) {
					return true;
				}
			}
		}
		return false;
	},

	makeLinksListFromOffices: function(officesList) {
		var marksList = [];
		
		for (var i in officesList) {
			var mark = {}, fields = {'id': 'id', 'pathname': 'url', 'title': 'name'};
			for (var j in fields)
				mark[fields[j]] = officesList[i][j];
			marksList.push(mark);
		}
		
		return this.makeLinksListFrom(marksList);
	},
	
	makeLinksListFrom: function(marksList){
		var links = [],
			offices = this;
		
		$.each(marksList, function(i, el){
			if (i < offices.groupPlacemarksLimit || (marksList.length <= offices.groupPlacemarksLimit + 2)) {
				links.push([
					'<a class="', offices.officesLinkClass,
					'" rel="', el.id,
					'" href="', el.url,
					'" onclick="$(document).trigger(\'show_office_modal\', this); return false;">', el.name,'</a>'
				].join(''))
			} else {
				var more   = marksList.length - offices.groupPlacemarksLimit,
				    form   = (more).getPluralForm("%d офис","%d офиса","%d офисов");
				links.push(["&hellip;и еще ", form, " &mdash; чтобы отобразить их, увеличьте масштаб карты"].join(''))
				return false;
			}
		})
		return links;
	},

	serializeOptions: function(o) {
		var opts = [], i;
		for (i in o) {
			opts[opts.length] = o[i];
		}
		return opts.join(';');
	},

	toggleIndicator: function(m) {
		$(this.officesForm)[m + 'Class']('loading-offices');
	},

	sendRequest: function() {
		var offices = this;

		offices.changingFilters = false;

		offices.toggleIndicator('add');

		$.getJSON(
			"/offices.action?action=search",
			{
				oaction: offices.options['oaction'],
				hour: offices.options['hour'],
				city: offices.options['city'],
				day: offices.options['day']
			},
			function(data) {
				offices.toggleIndicator('remove');
				offices.insertData(data);
				offices.filtersStack[offices.serializeOptions(offices.options)] = data;
			}
		);
	},

	// add metro anchor on the metroMap
	addMetro: function(metro, offices) {
		var a = this.addMetroA(this.metroMap, metro),
		    self = this;
		$(a).data('metroId', metro.id);
	},

	addMetroA: function(wAdd, metro) {
		var coords,
			a;

		if (metro.coords && metro.offices.length) {
			a = document.createElement('a');
			a.title = metro.title;
			//a.href = office.pathname;
			a.style.left = (parseInt(metro.coords[0]) - 6) + 'px';
			a.style.top = (parseInt(metro.coords[1]) - 6) + 'px';
			a.style.cursor = 'pointer';
			$(a).data('offices', metro.offices);
			
			wAdd.appendChild(a);
		}

		return a;
	},
	
	clickMetroA: function(e) {
		
	},

	addTR: function(wAdd, office) {
		var tr = document.createElement('tr'),
			offices = this,
			metro = office.metro || {},
			td, s, dd;

		tr.rel = office.id;
		
		/* first col */
		td = document.createElement('th');
		if (!metro.region && metro.title) {
			td.className = "metro";
		}
		if (metro.color) {
			td.style.color = "#" + metro.color;
		}

		if (metro.title) {
			td.appendChild(document.createTextNode(metro.title));
		}

		tr.appendChild(td);

		td = document.createElement('td');
		td.className = "address";

		s           = document.createElement('a');
		s.innerHTML = office.title;
		s.title     = metro.title;
		s.href      = office.pathname;
		s.className = 'show_office_modal';
		s.rel       = office.id;

		/*s.onclick = function() {
			offices.addModalContent(office);

			if (offices.favouriteBrowser) {
				offices.selects.css('visibility', 'hidden');
			}
			return false;
		};*/
		
		td.appendChild(s);
		td.appendChild(document.createElement('br'));

		if(office.shop) {
			dd = document.createElement('div');
			dd.innerHTML = office.shop;
			td.appendChild(dd);
		}
		tr.appendChild(td);

		td = document.createElement('td');
		td.className = "hours";
		td.innerHTML = office.works;
		tr.appendChild(td);

		wAdd.appendChild(tr);
	},

	addPoint: function(office) {
		if (office.coords) {
			var coords = office.coords.split(":");
			if (coords.length === 2) {
				this.markers[office.id] = {id: office.id, name: office.title, url: office.pathname, coords: {x: coords[0], y: coords[1]}};
			}
		}	
	},

	bindDisplayModal: function(offices){
		$(document).bind('show_office_modal', function(e, element){
			var id = $(element).attr('rel');
			offices.addModalContent(Office.data[id]);
			return false;
		});
		
		$('a.show_office_modal').live('click', function(e){
			$(document).trigger('show_office_modal', this);
			return false;
		})
	},

	//REMOVEME: Unused ?
	fixHeight: function() {
		$(this.modalWindow).css('height', $(this.modalWindow).height() + 'px');
	}
};

(new officesControl()).init();

