function YandexMapInterface(element, options) {
	this.element = element;
	this.markerMap = {};
	this._rangeBoundMap = {};
	this._areaMap = {};
	this._options = (options) ? options : {};	
}

YandexMapInterface.prototype = {

	_notifyMapMove : function() {
		if(this.mapChangeHandler) {
			var center = this.map.getCenter();
			this.mapChangeHandler.handler.call(this.mapChangeHandler.scope, center.getY(), center.getX());
		}
	},

	_notifyBoundsChange : function() {
	
		if(this._stopBoundsNotify) {
			console.debug('suppress _notifyBoundsChange');
			return;
		}	
	
		if(this.mapBoundsChangeHandler) {
			var center = this.map.getCenter();
			var bounds = this.map.getBounds();
			
			var dlat = (bounds.getTop()-bounds.getBottom())*0.5;
			var dlng = (bounds.getRight()-bounds.getLeft())*0.5;
//			this.mapBoundsChangeHandler.handler.call(this.mapBoundsChangeHandler.scope, bounds.getBottom(), bounds.getLeft(), bounds.getTop(), bounds.getRight());
			this.mapBoundsChangeHandler.handler.call(
				this.mapBoundsChangeHandler.scope,
				center.getY()-dlat, center.getX()-dlng, center.getY()+dlat, center.getX()+dlng
			);
		}
	},
	
	_notifyDragEnd : function() {
		if(this.dragEndHandler) this.dragEndHandler.handler.call(this.dragEndHandler.scope);
	},

	_notifyViewModeChange : function() {
		var type = this.map.getType();
		var mode = "normal";
		
		if(type==YMaps.MapType.HYBRID) {
			mode = "satellite";
		}
		
		console.debug('Switch yandex view mode to', mode);
		
		this.viewModeChangeHandler.handler.call(this.viewModeChangeHandler.scope, mode);		
	},

	_notifyMapClick : function(lat, lng) {
		if(this.mapClickHandler) this.mapClickHandler.handler.call(this.mapClickHandler.scope, lat, lng);
	},
	
	_notifyMarkerMove : function(name) {
		if(this.mapMarkerMoveHandler) {
			var pos = this.markerMap[name].item.getGeoPoint();
			this.mapMarkerMoveHandler.handler.call(this.mapMarkerMoveHandler.scope, name, pos.getY(), pos.getX());
		}
	},	

	_notifyMarkerClick : function(name) {
	
		var marker = this.markerMap[name];
		
		if(this.mapMarkerClickHandler) {
			this.mapMarkerClickHandler.handler.call(this.mapClickHandler.scope, name);
		}
	},
	
	_notifyBalloonClose : function() {
		this.mapBalloonCloseHandler.handler.call(this.mapBalloonCloseHandler.scope);
	},

	_setCenter : function() {
		if(this.mapCenter) this.map.setCenter(new YMaps.GeoPoint(this.mapCenter.lng, this.mapCenter.lat));
	},
	
	_shiftCenter : function() {
		if(!this.mapCenter || !this.map) return;
		this.map.panTo(new YMaps.GeoPoint(this.mapCenter.lng, this.mapCenter.lat));
	},	

	_setBounds : function() {
		if(!this.mapBounds || !this.map) return;
		this._stopBoundsNotify = true;
		
		var bounds = new YMaps.GeoBounds(new YMaps.GeoPoint(this.mapBounds.west, this.mapBounds.south), new YMaps.GeoPoint(this.mapBounds.east, this.mapBounds.north));
		
		console.debug('YANDEX set bounds');
		this.map.setBounds(bounds); // при установке границ масштабирование не всегда происходит как надо
		
		this.map.setCenter(
			new YMaps.GeoPoint(
					(this.mapBounds.west+this.mapBounds.east)*0.5,
					(this.mapBounds.south+this.mapBounds.north)*0.5
				)
			);
		
		console.debug('YANDEX set zoom');
//		this.map.setZoom(bounds.getMapZoom(this.map));
		
		this._stopBoundsNotify = false;
	},	

	setCenter : function(lat,lng) {
		this.mapCenter = { lat : lat, lng : lng };
		this._setCenter();
	},
	
	shiftCenter : function(lat,lng) {
		this.mapCenter = { lat : lat, lng : lng };
		this._shiftCenter();
	},

	setBounds : function(south, west, north, east) {
		this.mapBounds = { south : south, west : west, north : north, east : east };
		this._setBounds();
	},

	getViewState : function() {
		var bounds = this.map.getBounds();
		var center = bounds.getCenter();
		return { south : bounds.getBottom(), west : bounds.getLeft(), north : bounds.getTop(), east : bounds.getRight(), lat : center.getY(), lng : center.getX() };
	},

	setMapMoveHandler : function(scope,handler) {
		this.mapChangeHandler = { scope : scope, handler : handler };
	},

	setBoundsChangeHandler : function(scope,handler) {
		this.mapBoundsChangeHandler = { scope : scope, handler : handler };
	},
	
	setDragEndHandler : function(scope, handler) {
		this.dragEndHandler = { scope : scope, handler : handler };
	},	

	setClickHandler : function(scope, handler) {
		this.mapClickHandler = { scope : scope, handler : handler };
	},

	setViewModeChangeHandler : function(scope, handler) {
		this.viewModeChangeHandler = { scope : scope, handler : handler };
	},

	setMarkerMoveHandler : function(scope,handler) {
		this.mapMarkerMoveHandler = { scope : scope, handler : handler };
	},	

	setMarkerClickHandler : function(scope,handler) {
		this.mapMarkerClickHandler = { scope : scope, handler : handler };
	},

	setMarkerOverHandler : function(scope,handler) {
		this.mapMarkerOverHandler = { scope : scope, handler : handler };
	},
	
	setBalloonCloseHandler : function(scope,handler) {
		this.mapBalloonCloseHandler = { scope : scope, handler : handler };
	},

	removeMarker : function(name) {
	
		console.debug('YAN REM MARK', name);
	
		if(name in this.markerMap) {
			var marker = this.markerMap[name].item;
			delete this.markerMap[name];
			this.map.removeOverlay(marker);
		}
		
	},

	setupMarker : function(name, lat, lng, options) {
		var point = new YMaps.GeoPoint(lng, lat);
		
		if(name in this.markerMap) {
			this.map.removeOverlay(this.markerMap[name].item);
			console.debug('YAN Replace marker', name);
		}

		nativeOptions = {
			draggable : (options.moveable) ? true : false,
			hasHint : (options.title) ? true : false,
			hasBalloon : (options.html) ? true : false,
			hideIcon : false,
			clickable : true
//			style: "default#mailPostIcon"
//			balloonOptions: {
//				maxWidth: 70,
//				hasCloseButton: false,
//				mapAutoPan: 0
//			}			
		};

		if(options.image) {
			var style = new YMaps.Style();
			
			style.iconStyle = new YMaps.IconStyle();
			
			style.iconStyle.href = options.image.url;
			style.iconStyle.size = new YMaps.Size(options.image.width, options.image.height);
//			style.iconStyle.offset = new YMaps.Point(options.image.x - options.image.width/2, options.image.y - options.image.height/2);
			style.iconStyle.offset = new YMaps.Point(-options.image.width*0.5 + options.image.x, -options.image.height*0.5 + options.image.y);

			if(options.shadow) {
				style.iconStyle.shadow = new YMaps.IconShadowStyle();
				style.iconStyle.shadow.href = options.shadow.url;
				style.iconStyle.shadow.size = new YMaps.Size(options.shadow.width, options.shadow.height);
				style.iconStyle.shadow.offset = new YMaps.Point(-options.shadow.width*0.5 + options.shadow.x, -options.shadow.height*0.5 + options.shadow.y);
			}

			nativeOptions.style = style;
		}


		var marker = {}
		
		marker.item = new YMaps.Placemark(point, nativeOptions);
		marker.options = options;
		
		if(options.title) marker.item.setHintContent(options.title);
		if(options.html) marker.item.setBalloonContent(options.html);

		this.markerMap[name] = marker;
		this.map.addOverlay(marker.item);	
		
//		marker.item.setIconContent('<img src="http://maps.gstatic.com/intl/ru_ru/mapfiles/marker.png">');
		
		var scope = this;		
		
		if(options.moveable) {
			YMaps.Events.observe(marker.item, marker.item.Events.DragEnd, function (obj) { scope._notifyMarkerMove(name); } );
		}	

		YMaps.Events.observe(marker.item, marker.item.Events.Click, function (obj) { scope._notifyMarkerClick(name); } );
		
	},

	setRangeBound : function(name, south, west, north, east, color) {
		var object = this._rangeBoundMap[name];
		if(object) this.map.removeOverlay(object);

		object = new YMaps.Polyline([
				new YMaps.GeoPoint(west, south),
				new YMaps.GeoPoint(east, south),
				new YMaps.GeoPoint(east, north),
				new YMaps.GeoPoint(west, north),
				new YMaps.GeoPoint(west, south)
			]);

		this._rangeBoundMap[name] = object;
		this.map.addOverlay(object);
		console.debug('Bounds overlay added');
	},
	
	setArea : function(name, points) {
		var object = this._areaMap[name];
		if(object) this.map.removeOverlay(object);

		var data = new Array(points.length/2);
		
		var idx = 0;
		var i = 0;
		
		while(idx<data.length) { var lat = points[i++]; var lng = points[i++]; data[idx++] = new YMaps.GeoPoint(lng, lat); }
		
		object = new YMaps.Polyline(data);
		
		this._areaMap[name] = object;
		this.map.addOverlay(object);
	},

	clear : function() {
		this.map.removeAllOverlays();
		this.markerMap = {};
		this._rangeBoundMap = {};
		this._areaMap = {};
	},
	
	show : function() {
		$(this.element).css('left', 0);
//		this.element.style.display = "block";
		if(!this.map) {
			this.map = new YMaps.Map(this.element);
//			this.map.addControl(new YMaps.TypeControl());
//			this.map.addControl(new YMaps.ToolBar());

//			this.map.setMaxZoom(16);
			this.map.setMinZoom(3);

			var zoom = new YMaps.Zoom();
			
			this.map.addControl(zoom, new YMaps.ControlPosition(YMaps.ControlPosition.TOP_LEFT, new YMaps.Point(5, 5)));
			
			var search = new YMaps.SearchControl();
			
			if(this._options.search) {
				this.map.addControl(search, new YMaps.ControlPosition(YMaps.ControlPosition.BOTTOM_LEFT, new YMaps.Point(5, 20)));
			}

/*
			var zoom = new YMaps.Zoom({
				customTips: [
					{ index: 1, value: "Мелко" },
					{ index: 9, value: "Средне" },
					{ index: 16, value: "Крупно" }
				]
			});
			this.map.addControl(zoom);
*/			
//			var miniMap = new YMaps.MiniMap();
//			this.map.addControl(miniMap);
//			miniMap.setVisible(false);

			this.map.enableScrollZoom({smooth:true});
			
			this.map.addControl(new YMaps.ScaleLine());
			this._setBounds();
			this._setCenter();
			
			YMaps.Events.observe(this.map, this.map.Events.BalloonClose, function (map, mEvent) { this._notifyBalloonClose(); }, this);
			YMaps.Events.observe(this.map, this.map.Events.DragEnd, function (map, mEvent) { this._notifyDragEnd(); }, this);
			YMaps.Events.observe(this.map, this.map.Events.BoundsChange, function (map) { this._notifyBoundsChange(); }, this);
			YMaps.Events.observe(this.map, this.map.Events.Click, function (map, mEvent) { var p = mEvent.getGeoPoint(); this._notifyMapClick(p.getY(), p.getX()); }, this);
			YMaps.Events.observe(this.map, this.map.Events.TypeChange, function (map) { this._notifyViewModeChange(); }, this);
		}
	},
	
	hide : function() {
		$(this.element).css('left', -$(this.element).outerWidth());
//		this.element.style.display = "none";
	},
	
	setViewMode : function(mode) {
		var type = YMaps.MapType.MAP;
		if(mode=="satellite") { type = YMaps.MapType.HYBRID; }
		this.map.setType(type);
	},

	showBalloon : function(content, lat, lng) {
		if(content) {
			this.map.openBalloon(new YMaps.GeoPoint(lng, lat), content);
		} else {
			this.map.closeBalloon();
		}
	},
	
	clearBalloon : function() {
	}
	
}
