function AreaControl(callback, geb_id, kd_id) {
	this.globals = {
		draggingOn: false,
		borderCorrection: 0,
		areas: []
	};

	//box style options
	this.globals.style = {
		opacity: .2,
		fillColor: "#000",
		border: "3px solid blue"
	};

	// set callback
	this.globals.callback = callback;
	this.globals.geb_id = geb_id;
	this.globals.kd_id = kd_id;

	var style = this.globals.style;
	var borderStyleArray = style.border.split(' ');
	style.outlineWidth = parseInt(borderStyleArray[0].replace(/\D/g,''));
	style.outlineColor = borderStyleArray[2];
	style.sampleOutlineWidth = "3";
	style.sampleOutlineColor = "red";
	style.deselectedOpacity = 0.4;
	style.selectedOpacity = 1;
	style.alphaIE = 'alpha(opacity=' + (style.opacity * 100) + ')';
};

AreaControl.prototype = new GControl();

AreaControl.prototype.initialize = function(map) {
	var G = this.globals;
	var me = this;

	// a div for the buttons
	var buttons = document.createElement("div");

	var buttonDeleteArea = document.createElement("div");
	this.setButtonStyle_(buttonDeleteArea);
	buttonDeleteArea.style.display='none';
	buttons.appendChild(buttonDeleteArea);
	buttonDeleteArea.appendChild(document.createTextNode("Gebiet löschen"));
	GEvent.addDomListener(buttonDeleteArea, "click", function(e) {
		me.buttonDeleteAreaPressed_(e);
	});
	G.buttonDeleteArea = buttonDeleteArea;

	var buttonTransfer = document.createElement("div");
	this.setButtonStyle_(buttonTransfer);
	buttonTransfer.style.display='none';
	buttons.appendChild(buttonTransfer);
	buttonTransfer.appendChild(document.createTextNode("Gebiet übernehmen"));
	GEvent.addDomListener(buttonTransfer, "click", function(e) {
		me.buttonTransferPressed_(e);
	});
	G.buttonTransfer = buttonTransfer;

	var buttonNewArea = document.createElement("div");
	this.setButtonStyle_(buttonNewArea);
	buttons.appendChild(buttonNewArea);
	buttonNewArea.appendChild(document.createTextNode("neues Gebiet"));
	GEvent.addDomListener(buttonNewArea, "click", function(e) {
		me.buttonNewAreaPressed_(e);
	});
	G.buttonNewArea = buttonNewArea;

	map.getContainer().appendChild(buttons);

	// a div for the drawing area
	var drawingArea = document.createElement("div");
	var createDivs = ['outlineDiv', 'cornerTopDiv', 'cornerLeftDiv', 'cornerRightDiv', 'cornerBottomDiv'];
	for (var i=0; i<createDivs.length; i++) {
		var id = createDivs[i];
		var div = document.createElement("div");
	AreaControlUtil.style([div], {position:'absolute', display:'none'});
		drawingArea.appendChild(div);
		G[id] = div;
	}

	AreaControlUtil.style([drawingArea], {position: 'absolute', display: 'none', overflow: 'hidden', cursor: 'crosshair', zIndex: 101});

	GEvent.addDomListener(drawingArea, 'mousedown', function(e) {
		me.mousedown_(e);
	});
	GEvent.addDomListener(document, 'mousemove', function(e) {
		me.mousemove_(e);
	});
	GEvent.addDomListener(document, 'mouseup', function(e) {
		me.mouseup_(e);
	});

	map.getContainer().appendChild(drawingArea);
	G.borderCorrection = G.style.outlineWidth * 2;

	// globals
	G.map = map;
	G.drawingArea = drawingArea;

	this.initStyles_();

	return buttons;
};

AreaControl.prototype.getPolylineArray_ = function(area) {
	var nw = new GLatLng(area.y2, area.x2);
	var ne = new GLatLng(area.y2, area.x1);
	var se = new GLatLng(area.y1, area.x1);
	var sw = new GLatLng(area.y1, area.x2);
	return [nw, ne, se, sw, nw];
}

AreaControl.prototype.setAreas = function(areas) {
	var G = this.globals;
	var me = this;

	// remove old areas
	for (var i in G.areas) {
		G.map.removeOverlay(areas[i].poly);
	}

	// redraw sampleAreas
	for (var i in areas) {
		var area = areas[i];
		if (area.type == "sample") {
			area.poly = new GPolyline(me.getPolylineArray_(area), G.style.sampleOutlineColor, G.style.sampleOutlineWidth, G.style.deselectedOpacity);
		} else {
			area.poly = new GPolyline(me.getPolylineArray_(area), G.style.outlineColor, G.style.outlineWidth, G.style.deselectedOpacity);
		}
		G.map.addOverlay(area.poly);
		(function(area) {
			GEvent.addDomListener(area.poly, 'click', function(e) {
				me.selectArea_(area);
			});
		})(area);

		if (area.type == "sample") {
			var redIcon = new GIcon(G_DEFAULT_ICON);
			redIcon.image = "http://gmaps-samples.googlecode.com/svn/trunk/markers/red/blank.png";
			var marker = new GMarker(new GLatLng(area.y2, area.x1), {icon: redIcon});
			(function(area, marker) {
				GEvent.addListener(marker, "click", function() {
					me.selectArea_(area);
					marker.openInfoWindowHtml('<h4>' + area.name + '</h4><button onclick="AreaSelectService.setArea(' + area.id + ')">Gebiet übernehmen</button>');
				});
			})(area, marker);
			G.map.addOverlay(marker);
		} else {
			var blueIcon = new GIcon(G_DEFAULT_ICON);
			blueIcon.image = "http://gmaps-samples.googlecode.com/svn/trunk/markers/blue/blank.png";
			var marker = new GMarker(new GLatLng(area.y2, area.x1), {icon: blueIcon});
			(function(area, marker) {
				GEvent.addListener(marker, "click", function() {
					me.selectArea_(area);
					marker.openInfoWindowHtml('<button onclick="AreaSelectService.setArea(' + area.id + ')">Gebiet übernehmen</button>');
				});
			})(area, marker);
			G.map.addOverlay(marker);
		}
		
		if (area.id == G.geb_id) {
			me.selectArea_(area);
		}
	}

	G.areas = areas;

	me.setZoomAndPosition_();
}

AreaControl.prototype.setZoomAndPosition_ = function() {
	var G = this.globals;

	if (G.currentArea) {
		// zoom to the currently selected area
		var north = G.currentArea.y2;
		var east = G.currentArea.x2;
		var south = G.currentArea.y1;
		var west = G.currentArea.x1;
		G.map.setCenter(new GLatLng((south + north) / 2, (east + west) / 2));
		var latLngBounds = new GLatLngBounds(new GLatLng(south,west), new GLatLng(north,east))
		var zoomLevel = G.map.getBoundsZoomLevel(latLngBounds);
		G.map.setZoom(zoomLevel);
	} else if (G.sampleAreas && G.sampleAreas.length > 0) {
		var south = AreaControlUtil.getMinAttrValue(G.areas, "y1");
		var west = AreaControlUtil.getMinAttrValue(G.areas, "x1");
		var north = AreaControlUtil.getMaxAttrValue(G.areas, "y2");
		var east = AreaControlUtil.getMaxAttrValue(G.areas, "x2");
		G.map.setCenter(new GLatLng((south + north) / 2, (east + west) / 2));
		var latLngBounds = new GLatLngBounds(new GLatLng(south,west), new GLatLng(north,east))
		var zoomLevel = G.map.getBoundsZoomLevel(latLngBounds);
		G.map.setZoom(zoomLevel);
	}
}

// By default, the control will appear in the top left corner of the
// map with 7 pixels of padding.
AreaControl.prototype.getDefaultPosition = function() {
	return new GControlPosition(G_ANCHOR_BOTTOM_LEFT, new GSize(7, 7));
}

// Sets the proper CSS for the given button element.
AreaControl.prototype.setButtonStyle_ = function(button) {
	button.style.textDecoration = "underline";
	button.style.color = "#0000cc";
	button.style.backgroundColor = "white";
	button.style.font = "small Arial";
	button.style.border = "1px solid black";
	button.style.padding = "2px";
	button.style.marginBottom = "3px";
	button.style.textAlign = "center";
	button.style.width = "6em";
	button.style.cursor = "pointer";
	button.style.zIndex = 200;
}

/**
 * Set the cover sizes according to the size of the map
 */
AreaControl.prototype.setDimensions_ = function() {
	var G = this.globals;
	var mapSize = G.map.getSize();
	G.mapWidth  = mapSize.width;
	G.mapHeight = mapSize.height;
	// set left:0px in next <div>s in case we inherit text-align:center from map <div> in IE.
	AreaControlUtil.style([G.drawingArea, G.cornerTopDiv, G.cornerRightDiv, G.cornerBottomDiv, G.cornerLeftDiv], 
		{top: '0px', left: '0px', width: G.mapWidth + 'px', height: G.mapHeight +'px'});
};

/**
 * Initializes styles based on global parameters
 */
AreaControl.prototype.initStyles_ = function(){
	var G = this.globals;
	AreaControlUtil.style([G.drawingArea, G.cornerTopDiv, G.cornerRightDiv, G.cornerBottomDiv, G.cornerLeftDiv], 
		{filter: G.style.alphaIE, opacity: G.style.opacity, background:G.style.fillColor});
	G.outlineDiv.style.border = G.style.border;  
};

AreaControl.prototype.buttonNewAreaPressed_ = function(e) {
	var G = this.globals;
	var me = this;

	G.currentArea = null;

	me.deselectAreas_();
	if (G.drawingArea.style.display == 'block') { // reset if clicked before dragging 
		this.resetDragZoom_();
	} else {
		this.initCover_();
	}
};

AreaControl.prototype.buttonTransferPressed_ = function(e) {
	var G = this.globals;
	G.callback(G.currentArea.id);
};

AreaControl.prototype.buttonDeleteAreaPressed_ = function(e) {
	var G = this.globals;
	var me = this;

	var url = "http://www.landcare-dss.de/backend/deletearea?geb_id=" + G.currentArea.id;

	$.ajax({
		url:url,
		dataType:"text",
		success:function(data, textStatus) {
			G.map.removeOverlay(G.currentArea.poly);
			me.deselectAreas_();
		}
	});
};

/**
 * Shows the cover over the map
 */
AreaControl.prototype.initCover_ = function() {
	var G = this.globals;
	G.mapPosition = AreaControlUtil.getElementPosition(G.map.getContainer());
	this.setDimensions_();
	//this.setButtonMode_('zooming');
	AreaControlUtil.style([G.drawingArea], {display: 'block', background: G.style.fillColor});
	AreaControlUtil.style([G.outlineDiv], {width: '0px', height: '0px'});
};

AreaControl.prototype.mousedown_ = function(e) {
	var G = this.globals;
	var pos = this.getRelPos_(e);
	G.startX = pos.left;
	G.startY = pos.top;

	AreaControlUtil.style([G.drawingArea], {background: 'transparent', opacity: 1, filter: 'alpha(opacity=100)'});
	AreaControlUtil.style([G.outlineDiv], {left: G.startX + 'px', top: G.startY + 'px', display: 'block', width: '1px', height: '1px'});
	G.draggingOn = true;
	
	var left = G.startX;
	var top = G.startY;

	G.cornerTopDiv.style.display ='block';
	G.cornerTopDiv.style.top = 0 + 'px';
	G.cornerTopDiv.style.height = top + 'px';
	//G.cornerTopDiv.style.background ='red';

	G.cornerLeftDiv.style.left = (G.startX - G.mapWidth) +'px';
	G.cornerLeftDiv.style.top = G.startY + 'px';
	G.cornerLeftDiv.style.display = 'block';
	// G.cornerLeftDiv.style.background ='yellow';

	G.cornerRightDiv.style.left = G.startX + 'px';
	G.cornerRightDiv.style.top = G.startY + 'px';
	G.cornerRightDiv.style.display = 'block';
	// G.cornerRightDiv.style.background ='blue';

	G.cornerBottomDiv.style.left = G.startX + 'px';
	G.cornerBottomDiv.style.top = G.startY + 'px';
	G.cornerBottomDiv.style.width = '0px';
	G.cornerBottomDiv.style.display = 'block';
	// G.cornerBottomDiv.style.background ='green';

	return false;
};

AreaControl.prototype.mousemove_ = function(e) {
	var G = this.globals;
	if(G.draggingOn) {
		var pos = this.getRelPos_(e);

		var startX = G.startX;
		var startY = G.startY;
		var endX = pos.left;
		var endY = pos.top;
	
		var left = startX;
		var top = startY;
		var width = endX - startX;
		var height = endY - startY;
		if (endX < startX) {
			left = endX;
			width = startX - endX;
		}
		if (endY < startY) {
			top = endY;
			height = startY - endY;
		}

		AreaControlUtil.style([G.outlineDiv], {
			left: left + 'px',
			top: top  + 'px',
			display: 'block',
			width: (width - G.borderCorrection) < 0 ? 0 : (width - G.borderCorrection) + 'px',
			height: (height - G.borderCorrection) < 0 ? 0 : (height - G.borderCorrection) + 'px'
		});	

		G.cornerTopDiv.style.top = 0 + 'px';
		G.cornerTopDiv.style.height = top + 'px';
		G.cornerLeftDiv.style.top = top + 'px';
		G.cornerLeftDiv.style.left = 0 + 'px';
		G.cornerLeftDiv.style.width = left + 'px';
		G.cornerRightDiv.style.top = top + 'px';
		G.cornerRightDiv.style.left = left + width + 'px';
		G.cornerBottomDiv.style.top = top + height + 'px';
		G.cornerBottomDiv.style.left = left + 'px';
		G.cornerBottomDiv.style.width = width + 'px';

		return false;
	}  
};

AreaControl.prototype.mouseup_ = function(e) {
	var G = this.globals;
	var me = this;

	if (G.draggingOn) {
		var pos = this.getRelPos_(e);
		G.draggingOn = false;

		var rect = this.getRectangle_(G.startX, G.startY, pos, G.mapRatio);

		if (rect.left) rect.endX = rect.startX - rect.width;
		if (rect.top) rect.endY = rect.startY - rect.height;

		this.resetDragZoom_();

		var sw = G.map.fromContainerPixelToLatLng(new GPoint(rect.startX, pos.top));
		var ne = G.map.fromContainerPixelToLatLng(new GPoint(pos.left, rect.startY));

		var area = {x1:sw.x, y1:sw.y, x2:ne.x, y2:ne.y, type:"user"};
		area.poly = new GPolyline(me.getPolylineArray_(area), G.style.outlineColor, G.style.outlineWidth, G.style.deselectedOpacity);

		GEvent.addDomListener(area.poly, 'click', function(e) {
			me.selectArea_(area);
		});

		var intersects = false;
		for (var i in G.areas) {
			var compareArea = G.areas[i];
			if (compareArea.type == "sample" && area.poly.getBounds().intersects(compareArea.poly.getBounds())) {
				area.identifier = compareArea.identifier;
				intersects = true;
				break;
			}
		}
		if (sw.x===ne.x && sw.y===ne.y) {
			// es wurde keine Fläche, sondern nur ein Punkt gezeichnet
			alert("Um ein neues Gebiet anzulegen, klicken Sie zunächst auf 'neues Gebiet'. Dann klicken Sie mit der linken Maustaste in eine Ecke des Gebiets, halten diese gedrückt und ziehen durch Bewegen der Maus ein neues Gebiet auf. Beim Loslassen der Maustaste wird das Gebiet angelegt.");
		} else if (!intersects) {
			alert("Das Auswahlgebiet muss in einem der rot markierten Testgebiete liegen!");
		} else {
			G.areas.push(area);
			G.map.addOverlay(area.poly);

			var url = "http://www.landcare-dss.de/backend/newarea?x1=" + area.x1 + "&y1=" + area.y1 + "&x2=" + area.x2 + "&y2=" + area.y2 + "&identifier=" + area.identifier;
			if (G.kd_id) {
				url = url + "&kd_id=" + G.kd_id;
			}

			$.ajax({
				url:url,
				dataType:"json",
				success:function(data, textStatus) {
					me.selectArea_(area);
					area.id = data.value;
				}
			});
		}
	}
};

AreaControl.prototype.selectArea_ = function(area) {
	var G = this.globals;
	var me = this;
	me.deselectAreas_();
	G.currentArea = area;
	area.poly.setStrokeStyle({opacity:G.style.selectedOpacity});
	G.buttonTransfer.style.display='block';
	//console.log(area);
	if (area.type == "user" && !area.additional) {
		G.buttonDeleteArea.style.display='block';
	} else {
		// sample areas cannot be deleted
		// additionally loaded areas (that do not belong to the users areas) cannot be deleted
		G.buttonDeleteArea.style.display='none';
	}
	G.map.closeInfoWindow();
}

AreaControl.prototype.deselectAreas_ = function() {
	var G = this.globals;
	G.currentArea = null;
	for (var i in G.areas) {
		var area = G.areas[i];
		area.poly.setStrokeStyle({opacity:G.style.deselectedOpacity})
	}
	G.buttonTransfer.style.display='none';
	G.buttonDeleteArea.style.display='none';
}

/**
 * Gets position of the mouse relative to the map
 * @param {Object} e
 */
AreaControl.prototype.getRelPos_ = function(e) {
	var pos = AreaControlUtil.getMousePosition(e);
	var G = this.globals;
	return {top: (pos.top - G.mapPosition.top), 
					left: (pos.left - G.mapPosition.left)};
};

/**
 * Figures out the rectangle the user's trying to draw
 * @param {Number} startX 
 * @param {Number} startY
 * @param {Object} pos
 * @param {Number} ratio
 * @return {Object} Describes the rectangle
 */
AreaControl.prototype.getRectangle_ = function(startX, startY, pos, ratio) {
	var left = false;
	var top = false;
	var dX = pos.left - startX;
	var dY = pos.top - startY;	
	if (dX < 0) {
		dX = dX * -1;
		left = true;
	}
	if (dY < 0) {
		dY = dY * -1;
		top = true;
	}
	delta = dX > dY ? dX : dY;

	return {
		startX: startX,
		startY: startY,
		endX: startX + delta,
		endY: startY + parseInt(delta * ratio),
		width: delta,
		height: parseInt(delta * ratio),
		left:left,
		top:top
	}
};

/** 
 * Resets CSS and button display when drag zoom done
 */
AreaControl.prototype.resetDragZoom_ = function() {
	var G = this.globals;
	AreaControlUtil.style([G.drawingArea, G.cornerTopDiv, G.cornerRightDiv, G.cornerBottomDiv, G.cornerLeftDiv], 
		{display: 'none', opacity: G.style.opacity, filter: G.style.alphaIE});
	G.outlineDiv.style.display = 'none';	

};

/* utility functions in AreaControlUtil.namespace */
var AreaControlUtil={};

/**
 * Gets position of element
 * @param {Object} element
 * @return {Object} Describes position
 */
AreaControlUtil.getElementPosition = function(element) {
	var leftPos = element.offsetLeft;          // initialize var to store calculations
	var topPos = element.offsetTop;            // initialize var to store calculations
	var parElement = element.offsetParent;     // identify first offset parent element  
	while (parElement != null ) {                // move up through element hierarchy
		leftPos += parElement.offsetLeft;      // appending left offset of each parent
		topPos += parElement.offsetTop;  
		parElement = parElement.offsetParent;  // until no more offset parents exist
	}
	return {left: leftPos, top: topPos};
};

/**
 * Applies styles to DOM objects 
 * @param {String/Object} elements Either comma-delimited list of ids 
 *   or an array of DOM objects
 * @param {Object} styles Hash of styles to be applied
 */
AreaControlUtil.style = function(elements, styles){
	if (typeof(elements) == 'string') {
		elements = AreaControlUtil.getManyElements(elements);
	}
	for (var i = 0; i < elements.length; i++){
		for (var s in styles) { 
			elements[i].style[s] = styles[s];
		}
	}
};

/**
 * Gets DOM elements array according to list of IDs
 * @param {String} elementsString Comma-delimited list of IDs
 * @return {Array} Array of DOM elements corresponding to s
 */
AreaControlUtil.getManyElements = function(idsString){
	var idsArray = idsString.split(',');
	var elements = [];
	for (var i = 0; i < idsArray.length; i++){
		elements[elements.length] = document.getElementById(idsArray[i])
	};
	return elements;
};

/**
 * A general-purpose function to get the absolute position
 * of the mouse.
 * @param {Object} e  Mouse event
 * @return {Object} Describes position
 */
AreaControlUtil.getMousePosition = function(e) {
	var posX = 0;
	var posY = 0;
	if (!e) var e = window.event;
	if (e.pageX || e.pageY) {
		posX = e.pageX;
		posY = e.pageY;
	} else if (e.clientX || e.clientY){
		posX = e.clientX + 
			(document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft);
		posY = e.clientY + 
			(document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);
	}
	return {left: posX, top: posY};
};

AreaControlUtil.getMinAttrValue = function(list, attr) {
	var value = null;
	for (var i in list) {
		if (value == null || list[i][attr] < value) {
			value = list[i][attr];
		}
	}
	return value;
};

AreaControlUtil.getMaxAttrValue = function(list, attr) {
	var value = null;
	for (var i in list) {
		if (value == null || list[i][attr] > value) {
			value = list[i][attr];
		}
	}
	return value;
};


