
// application modes
var ISPM_MODE_ITERATOR = 0000;
var ISPM_MODE_FREE = ISPM_MODE_ITERATOR++;
var ISPM_MODE_EDIT_AREA = ISPM_MODE_ITERATOR++;
var ISPM_MODE_EDIT_AREA_REMOVE_MARKERS = ISPM_MODE_ITERATOR++;

// area states
var ISPM_AREA_ITERATOR = 1000;
var ISPM_AREA_NO_ERRORS = ISPM_AREA_ITERATOR++;
var ISPM_AREA_ERROR_NAME_EMPTY = ISPM_AREA_ITERATOR++;
var ISPM_AREA_ERROR_NAME_RESERVED = ISPM_AREA_ITERATOR++;
var ISPM_AREA_ERROR_NAME_INVALID = ISPM_AREA_ITERATOR++;
var ISPM_AREA_ERROR_NO_MARKERS = ISPM_AREA_ITERATOR++;
var ISPM_AREA_ERROR_NO_NETWORK = ISPM_AREA_ITERATOR++;
var ISPM_AREA_ERROR_OUTSIDE_COUNTRY = ISPM_AREA_ITERATOR++;
var ISPM_AREA_ERROR_TOO_BIG = ISPM_AREA_ITERATOR++;
var ISPM_AREA_SAVED = ISPM_AREA_ITERATOR++;

// network states
var ISPM_NETWORK_ITERATOR = 2000;
var ISPM_NETWORK_NO_ERRORS = ISPM_NETWORK_ITERATOR++;
var ISPM_NETWORK_ERROR_NAME_EMPTY = ISPM_NETWORK_ITERATOR++;
var ISPM_NETWORK_ERROR_NAME_RESERVED = ISPM_NETWORK_ITERATOR++;
var ISPM_NETWORK_ERROR_NAME_INVALID = ISPM_NETWORK_ITERATOR++;
var ISPM_NETWORK_ERROR_NO_ID = ISPM_NETWORK_ITERATOR++;
var ISPM_NETWORK_ERROR_PROVIDER = ISPM_NETWORK_ITERATOR++;

// address search states
var ISPM_ADDRESS_ITERATOR = 3000;
var ISPM_ADDRESS_FOUND = ISPM_ADDRESS_ITERATOR++;
var ISPM_ADDRESS_NOT_FOUND = ISPM_ADDRESS_ITERATOR++;
var ISPM_ADDRESS_CANCELED = ISPM_ADDRESS_ITERATOR++;

// point search states
var ISPM_POINT_ITERATOR = 4000;
var ISPM_POINT_FOUND = ISPM_POINT_ITERATOR++;
var ISPM_POINT_FOUND_MANY = ISPM_POINT_ITERATOR++;
var ISPM_POINT_NOT_FOUND = ISPM_POINT_ITERATOR++;
var ISPM_POINT_INSIDE_COUNTRY = ISPM_POINT_ITERATOR++;
var ISPM_POINT_OUTSIDE_COUNTRY = ISPM_POINT_ITERATOR++;

// area history types
var ISPM_AREA_HISTORY_ITERAOR = 5000;
var ISPM_AREA_HISTORY_MARKER_ADD = ISPM_AREA_HISTORY_ITERAOR++;
var ISPM_AREA_HISTORY_MARKER_REMOVE = ISPM_AREA_HISTORY_ITERAOR++;
var ISPM_AREA_HISTORY_MARKER_DRAG = ISPM_AREA_HISTORY_ITERAOR++;

var ISPM = mint.Base.extend({
	map: null,				// map object
	mode: ISPM_MODE_FREE,			// application mode
	
	initLat: 0,				// initial latitude
	initLng: 0,				// initial length
	initZoom: 0,				// initial map zoom
	initType: G_NORMAL_MAP,			// initial map type
	initAddr: "",				// initial map address
	
	minZoom: 0,				// minimum map zoom
	maxZoom: 17,				// maximum map zoom
	
	geoCoder: null,				// geocoder object
	
	country: '',				// limit search to country
	lastAddr: '',				// last searched address
	
	cancelAddrSearch: false,		// cancel address search if in progress
	
	activeArea: null,			// active area object
	activeNetwork: null,			// active network object
	activeProvider: null,			// active provider object
	activeWidget: null,			// active widget object
	
	allowAreaEdit: false,			// allow area editing
	
	markersManager: null,			// markers manager used by all markers
	
	maxAreaSize: 80000000,			// max area size in m^2
	maxAreaMarkers: 10,			// maximum number of area markers
	
	pointers: {},				// contains all pointers on map
	pointersPath: '',			// root path for pointers icons (WITH trailing /)
	
	loaderEl: 'ispm-loader',		// container element for loader
	widgetEl: '',				// container element for widgets
	
	base_url: '',				// base url for ajax requests
	
	control: new GLargeMapControl3D(),	
	
	iconsPath: 'img/ispm/',
	
	icons: {
		editMarker: null,
		editMarkerImg: 'marker.gif',
		editMarkerActiveImg: 'marker_active.gif',
		editMarkerSelectedImg: 'marker_selected.gif'
	},
	
	areasStyle: {
		strokeColor: "#0099FF",
		strokeOpacity: 0.8,
		strokeWeight: 2,
		fillColor: "#0099FF",
		fillOpacity: 0.5,
		strokeHilightColor: "#FFFF00",
		strokeHilightOpacity: 0.8,
		strokeHilightWeight: 2,
		fillHilightColor: "#FFFF00",
		fillHilightOpacity: 0
	},
	
	__constructor: function(options) {
		var that = this;
		
		this.setOptions(options);
		
		if(GBrowserIsCompatible()) {
			// create gmap object and set init settings (center point, zoom and type)
			this.map = new GMap(mint.$(options.map));

			this.map.setCenter(new GLatLng(this.initLat, this.initLng), this.initZoom, this.initType);
			
			// limit zoom level
			for (var i = 0, mapTypes = this.map.getMapTypes(); i < mapTypes.length; i++) {
				mapTypes[i].getMinimumResolution = function() {return that.minZoom}
				mapTypes[i].getMaximumResolution = function() {return that.maxZoom}
			} 
			
			// add controls too map
			this.map.addControl(this.control);
			
			this.markersManager = new GMarkerManager(this.map);
			
			// create geocoder object (for address search)
			this.geoCoder = new GClientGeocoder();
			
			// set base country
			if(this.country) this.geoCoder.setBaseCountryCode(this.country);
			
			// set init address (cancel search by setting cancelAddrSearch to true)
			if(this.initAddr) this.findAddr(this.initAddr);
			
			// add events
			GEvent.addListener(this.map, 'click', function(overlay, point, overlayPoint) {
				that.onClick.call(that, overlay, point, overlayPoint)
			});
			
			GEvent.addListener(this.map, 'singlerightclick', function(point, src, overlay) {
				that.onRightClick.call(that, point, src, overlay);
			});
		}
		
		// create edit area marker icon
		this.icons.editMarker = new GIcon(G_DEFAULT_ICON, this.base_url + this.iconsPath + this.icons.editMarkerImg);

		this.icons.editMarker.iconSize = new GSize(16, 16);
		this.icons.editMarker.iconAnchor = new GPoint(14, 16);
		this.icons.editMarker.shadow = "";
	},
	
	// process mouse click event on map
	onClick: function(overlay, point, overlayPoint) {
		with(this) {
			switch(mode) {
				case ISPM_MODE_FREE: {
					break;
				}
				case ISPM_MODE_EDIT_AREA: {
					// if edited area has less markers than max allowed add new marker and redraw area
					if (activeArea.markers.length < maxAreaMarkers) {
						if(!overlay || !(overlay instanceof GMarker)) {
							activeArea.addMarker(overlay ? overlayPoint : point);
							activeArea.redraw();
						}
						else if(overlay instanceof GMarker) {
						//	overlay.area.selectMarker(overlay.index);
						}
					}
					else activeArea.callEvent('maxMarkers'); // else fire error (markers limit exceed)
					break;
				}
			}
		}
	},
	
	// process right mouse click event on map
	onRightClick: function(point, src, overlay) {
		with(this) {
			switch(mode) {
				case ISPM_MODE_FREE: {
					break;
				}
				case ISPM_MODE_EDIT_AREA: {
					// if clicked on overlay (area marker) remove it
					if(overlay && overlay instanceof GMarker) {
						activeArea.removeMarker(overlay);
					}
					
					break;
				}
			}
		}
	},
	
	// creates pointer used on map
	addPointer: function(name, icon, size, anchor) {
		var pointerIcon = new GIcon(G_DEFAULT_ICON, this.base_url + this.iconsPath + icon);
		
		pointerIcon.iconSize = size;
		pointerIcon.iconAnchor = anchor;
		
		var pointer = new GMarker(this.map.getCenter(), {
			clickable: false,
			draggable: false,
			icon: pointerIcon,
			zIndexProcess: function() {return 99}
		});
		
		this.pointers[name] = pointer;
		this.map.addOverlay(pointer);
		
		pointer.hide();

		return pointer;
	},
	
	// hides all pointers on map
	hidePointers: function() {
		for(var i in this.pointers) {
			this.pointers[i].hide()
		}
	},
	
	// get center point for given address (fire callback when done)
	getAddrPoint: function(addr, callback) {
		var that = this;
		
		if(!callback) callback = function() {};
		
		with(this) {
			geoCoder.getLocations(addr, function(result) {
				if(!result.Placemark || result.Placemark.length == 0) {
					callback(ISPM_POINT_NOT_FOUND, null, addr, result);
				}
				else if(result.Placemark.length == 1) {
					callback(ISPM_POINT_FOUND, new GLatLng(result.Placemark[0].Point.coordinates[1], result.Placemark[0].Point.coordinates[0]), addr, result);
				}
				else if(result.Placemark.length > 1) {
					callback(ISPM_POINT_FOUND_MANY, result.Placemark, addr, result);
				}
			});
		}
	},
	
	// center map on given address
	findAddr: function(addr, callback) {
		var that = this;
		
		if(!callback) callback = function() {};
		
		with(this) {
			cancelAddrSearch = false;
			
			getAddrPoint(addr, function(state, point) {
				if(that.cancelAddrSearch) {
					// address search canceled
					callback(ISPM_ADDRESS_CANCELED, addr, point);
				}
				else if(state == ISPM_POINT_FOUND) {
					// remeber searched address
					that.lastAddr = addr; 
					// center map to found address
					that.map.setCenter(point);
					callback(ISPM_ADDRESS_FOUND, addr, point);
				}
				else callback(ISPM_ADDRESS_NOT_FOUND, addr);
			});
		}
	},
	
	// check if given point is inside base country
	pointInCountry: function(point, callback) {
		var that = this;
		
		if(!callback) callback = function() {};
		
		this.geoCoder.getLocations(point, function(result) {
			// check found country code with base country
			if(result.Placemark.some(function(placemark) {
				if(placemark.AddressDetails) {
					if(placemark.AddressDetails.Country) {
						if(placemark.AddressDetails.Country.CountryNameCode) {
							if(placemark.AddressDetails.Country.CountryNameCode.toUpperCase() == this.country.toUpperCase()) {
								return true;
							}
						}
					}
				}
				
				return false;
			}, that)) {
				callback(ISPM_POINT_INSIDE_COUNTRY);
			}
			else callback(ISPM_POINT_OUTSIDE_COUNTRY);
		});
	},
	
	// get areas in bounds
	getAreasInBounds: function(callback, exclude) {
		var that = this;
		
		with(this) {
			if(!getAreasInBounds.loading) {
				var bounds = map.getBounds();
				
				getAreasInBounds.loading = true;
				
				new mint.Request({
					url: base_url + 'ispm_remote/get_areas_in_bounds',
					method: 'POST',
					
					params: {
						minLat: bounds.getSouthWest().lat(),
						maxLat: bounds.getNorthEast().lat(),
						minLng: bounds.getSouthWest().lng(),
						maxLng: bounds.getNorthEast().lng(),
						excludeIds: exclude
					},
					
					events: {
						done : function() {
							var areas = eval('('+this.response+')');
							
							that.getAreasInBounds.loading = false;
							
							callback(areas);
						}
					}
				});
			}
		}
	},
	
	// load widget
	loadWidget: function(widgetName, params) {
		var that = this;
		
		if(!params) params = {};
		
		// always pass widget name
		params.widget_name = widgetName;
		
		new mint.Request({
			url: this.base_url+'ispm_remote/load_widget',
			method: 'POST',
			
			evalScripts: true,
			target: that.widgetEl,
			
			params: params,
			
			events: {
				complete: function() {
					that.hideLoader();
				}
			}
		});
		
		this.showLoader();
	},
	
	showError: function(error_name) {
		(isString(error_name) ? $(error_name+"-error") : $(error_name)).show();
	},
	
	hideError: function(error_name) {
		(isString(error_name) ? $(error_name+"-error") : $(error_name)).hide();
	},
	
	hideErrors: function(delay) {
		var that = this, errors = mint.$$('div.error');
		
		errors.each(function(el) {
			that.hideError(el, delay);
		});
	},
	
	showLoader: function() {
		$(this.loaderEl).show();
	},
	
	hideLoader: function() {
		$(this.loaderEl).hide();
	},
	
	setActiveArea: function(area) {
		this.activeArea = area || null;
	},
	
	setActiveNetwork: function(network) {
		this.activeNetwork = network || null;
	},
	
	setActiveProvider: function(provider) {
		this.activeProvider = provider || null;
	},
	
	setActiveWidget: function(widget) {
		this.activeWidget = widget || null;
	}
});

var ISPM_Provider = mint.Base.extend({
	id: null,
	ispm: null,
	
	networks: [],
	
	__constructor: function(options) {
		this.setOptions(options);
	},
	
	addNetwork: function(network, toServer, callback) {
		this.networks.push(network);
		
		// assign ispm and provider object to network
		network.ispm = this.ispm;
		network.provider = this;
		
		// if added to server
		if(toServer) {
			var that = this;
			
			// empty name?
			if (!network.name) {
				callback(ISPM_NETWORK_ERROR_NAME_EMPTY);
				return false;
			}
			
			// show loader while checking network name
			this.ispm.showLoader();
			
			// check if network name isn't reserved
			new mint.Request({
				url: this.ispm.base_url + 'ispm_remote/network_exists',
				method: 'POST',
				
				params: {
					network_name: network.name
				},
				
				events: {
					done: function() {
						// hide loader
						that.ispm.hideLoader();
						
						// if network name isn't reserved
						if(this.response == 'ok') {
							// add network to server
							new mint.Request({
								url: that.ispm.base_url + 'ispm_remote/add_network',
								method: 'POST',
								
								params: {
									name: network.name,
									center_lat: network.centerPoint.lat(),
									center_lng: network.centerPoint.lng()
								},
								
								events: {
									done: function() {
										if(this.response == 'error_network_exists') {
											callback(ISPM_NETWORK_ERROR_NAME_RESERVED);
										}
										else if(this.response == 'error_name') {
											callback(ISPM_NETWORK_ERROR_NAME_INVALID);
										}
										else {
											callback(ISPM_NETWORK_NO_ERRORS, this.response);
										}
									}
								}
							});
						}
						else {
							callback(ISPM_NETWORK_ERROR_NAME_RESERVED);
						}
					}
				}
			})

		}
	},
	
	removeNetwork: function(network, fromServer) {
		network.remove(fromServer);
	},
	
	getNetworksEditData: function() {
		var that = this;
		
		new mint.Request({
			url: this.base_url+'ispm_remote/get_network_edit_data',
			getJSON: true,
			
			events: {
				done: function() {
					var networks = this.response;
					
					for(var i in networks) {
						var network_data = new ISPM_Network({
							id: networks[i].id,
							name: networks[i].name,
							center: new GLatLng(networks[i].center_lat, networks[i].center_lng)
						});
						
						if(networks[i].areas) {
							for(var i2 in networks[i].areas) {
								var area_data = new ISPM_Area({
									id: networks[i].areas[i2].id,
									name: networks[i].areas[i2].name
								});
								
								if(networks[i].areas[i2].points) {
									for(var i3 in networks[i].areas[i2].points) {
										area_data.addPoint(new ISPM_Point(networks[i].areas[i2].points[i3].id, networks[i].areas[i2].points[i3].lat, networks[i].areas[i2].points[3].lng));
									}
								}
								
								network_data.areas.push(area_data);
							}
						}
						
						that.networks.push(network_data);
					}
					
					that.callEvent("getNetworksEditDataDone");
				}
			}
		});	
	},
	
	saveBackup: function(callback) {
		var that = this;
		
		with(this) {
			new mint.Request({
				url: ispm.base_url + 'backend/save_backup',
				events: {
					done: function() {
						callback(this.response);
					}
				}
			})
		}
	}
});

var ISPM_Network = mint.Base.extend({
	id: null,
	ispm: null,
	provider: null,
	
	areas: [],
	baseArea: null,
	
	centerPoint: null,
	
	__constructor: function(options) {
		with(this) {
			setOptions(options);
		}
	},
	
	load: function(callback) {
		var that = this;
		
		new mint.Request({
			url: this.ispm.base_url + 'ispm_remote/load_network',
			method: 'POST',
			
			params: {
				network_id: this.id
			},
			
			events: {
				done: function() {
					var network = eval("("+this.response+")");
					
					that.id = network.id;
					that.name = network.name;
					that.baseArea = null;
					that.centerPoint = new GLatLng(parseFloat(network.center_lat), parseFloat(network.center_lng));
					
					that.ispm.cancelAddrSearch = true;
					
					if(!mint.isEmpty(network.areas)) {
						for(var i in network.areas) {
							// create area
							var area = new ISPM_Area({
								id: network.areas[i].id,
								name: network.areas[i].name,
								ispm: that.ispm,
								network: that
							});
							
							// add points to area
							for(var i2 = 0; i2 < network.areas[i].points.length; ++i2) {
								area.addMarker(new GLatLng(parseFloat(network.areas[i].points[i2].lat), parseFloat(network.areas[i].points[i2].lng)));
							}
							
							// add area to network
							that.addArea(area);
							
							// redraw area (add to map)
							area.redraw();
							
							if(network.base_area === area.id) {
								that.baseArea = area;
							}
							
						}
					}
					
					if(callback) callback(network);
				}
			}
		});
	},
	
	setInCenter: function() {
		this.ispm.map.setCenter(this.baseArea ? this.baseArea.getCenter() : this.centerPoint);
	},
	
	addArea: function(area) {
		this.areas.push(area);
		
		area.ispm = this.ispm;
		area.network = this;
	},
	
	removeArea: function(area, fromServer) {
		area.network = null;
		this.areas.remove(area);
	},
	
	getAreaByID: function(id) {
		var foundArea = null;
		
		this.areas.each(function(area) {
			if(area.id == id) foundArea = area;
		});
		
		return foundArea;
	},
	
	rename: function(name, callback) {
		if(!name) return callback(ISPM_NETWORK_ERROR_NAME_EMPTY);
		
		new mint.Request({
			url: this.ispm.base_url + 'ispm_remote/rename_network',
			method: 'POST',
			
			params: {
				network_id: this.id,
				network_name: name
			},
			
			events: {
				done: function() {
					if(this.response == 'ok') {
						callback(ISPM_NETWORK_NO_ERRORS);
					}
					else if(this.response == 'error_reserved') {
						callback(ISPM_NETWORK_ERROR_NAME_RESERVED);
					}
					else if(this.response == 'error_name') {
						callback(ISPM_NETWORK_ERROR_NAME_INVALID);
					}
				}
			}
		});
	},
	
	remove: function(fromServer) {
		var that = this;
		
		this.areas.each(function(area) {
			area.remove();
		});
		
		this.areas.clear();
		
		if(fromServer) {
			var that = this;
			
			if(!this.id) return;
			
			new mint.Request({
				url: this.ispm.base_url + 'ispm_remote/remove_network',
				method: 'POST',
				
				params: {
					network_id: that.id
				},
				
				events: {
					done: function() {
						that.callEvent('removed')
					}
				}
			});
		}
	}
});

// events: addMarker, removeMarker, dragStartMarker, dragEndMarker, remove
var ISPM_Area = mint.Base.extend({
	id: -1,
	name: "",
	
	ispm: null,
	network: null,

	poly: null,
	
	markers: [],
	savedMarkers: [],
	selectedMarkers: [],
	
	hilight: false,
	outline: false,
	editable: false,
	
	history: null,
	
	__constructor: function(options) {
		this.setOptions(options.ispm.areasStyle);
		this.setOptions(options);
	},
	
	edit: function() {
		with(this) {
			ispm.activeArea = this;
			ispm.mode = ISPM_MODE_EDIT_AREA;
			
			editable = true;
			hilight = true;
			
			redraw();
		}
	},
	
	free: function() {
		with(this) {
			ispm.activeArea = null;
			ispm.mode = ISPM_MODE_FREE;
			
			editable = false;
			hilight = false;
			
			redraw();
		}
	},
	
	save: function(toServer, callback) {
		var that = this;
		
		with(this) {
			// no network assigned?
			if(!network) return callback(ISPM_AREA_ERROR_NO_NETWORK);
			// no markers or less than 3?
			if(markers.length < 3) return callback(ISPM_AREA_ERROR_NO_MARKERS);
			// empty name?
			if(name.length == 0) return callback(ISPM_AREA_ERROR_NAME_EMPTY);
			
			// area too big?
			that.free();
			var area_size = poly.getArea();
			that.edit();
			
			if(area_size > ispm.maxAreaSize) {
				return callback(ISPM_AREA_ERROR_TOO_BIG, {area_size: area_size});
			}
			
			// outside country?
			ispm.pointInCountry(getCenter(), function(inside) {
				if(inside) {
					// area data sent to server
					var area_data = {
						id: that.id,
						name: that.name,
						network_id: that.network.id,
						size: that.poly.getArea(),
						center_lat: that.poly.getBounds().getCenter().lat(),
						center_lng: that.poly.getBounds().getCenter().lng(),
						points: []
					}
					
					// fetch points data
					for(var i = 0; i < that.markers.length; ++i) {
						area_data.points.push({
							pos: i, 
							lat: that.markers[i].getLatLng().lat(),
							lng: that.markers[i].getLatLng().lng()
						})
					}
					
					// save area on server
					new mint.Request({
						url: that.ispm.base_url + "ispm_remote/save_area",
						method: "POST",
						
						params: {
							area_data: mint.encodeJSON(area_data)
						},
						
						events: {
							done: function() {
								if(this.response == "error_name") {
									callback(ISPM_AREA_ERROR_NAME_INVALID);
								}
								else if(this.response == "error_markers") {
									callback(ISPM_AREA_ERROR_NO_MARKERS);
								}
								else if(this.response == "error_area_exists") {
									callback(ISPM_AREA_ERROR_NAME_RESERVED);
								}
								else {
									if(callback) callback(ISPM_AREA_SAVED);
									
									that.id = parseInt(this.response);
									that.free();
								}
							}
						}
					});
					/*
					// check if area with given name exists
					new mint.Request({
						url: that.ispm.base_url + "ispm_remote/area_exists",
						method: "POST",
						
						params: {
							area_id: that.id,
							area_name: that.name,
							network_id: that.network.id
						},
						
						events: {
							done: function() {
								// if everything is ok save area on server
								if(this.response == 'ok') {
									that.free();
									
									// area data sent to server
									var area_data = {
										id: that.id,
										name: that.name,
										network_id: that.network.id,
										size: that.poly.getArea(),
										center_lat: that.poly.getBounds().getCenter().lat(),
										center_lng: that.poly.getBounds().getCenter().lng(),
										points: []
									}
									
									// fetch points data
									for(var i = 0; i < that.markers.length; ++i) {
										area_data.points.push({
											pos: i, 
											lat: that.markers[i].getLatLng().lat(),
											lng: that.markers[i].getLatLng().lng()
										})
									}
									
									// save area on server
									new mint.Request({
										url: that.ispm.base_url + "ispm_remote/save_area",
										method: "POST",
										
										params: {
											area_data: mint.encodeJSON(area_data)
										},
										
										events: {
											done: function() {
												if(callback) callback(ISPM_AREA_SAVED);
												that.id = parseInt(this.response);
											}
										}
									});
								}
								else callback(ISPM_AREA_ERROR_NAME_RESERVED);
							}
						}
					});
					*/
				}
				else callback(ISPM_AREA_ERROR_OUTSIDE_COUNTRY);
			});
		}
	},
	
	cancel: function() {
		this.free();
	},
	
	redraw: function() {
		var that = this;
		
		with(this) {
			if(poly) ispm.map.removeOverlay(poly);
			
			if(hilight) poly = new GPolygon((editable ? getPoints() : getPoints().concat(getPoints()[0])), strokeHilightColor, strokeHilightWeight, strokeHilightOpacity, fillHilightColor, fillHilightOpacity);
			else poly = new GPolygon((editable ? getPoints() : getPoints().concat(getPoints()[0])), strokeColor, strokeWeight, strokeOpacity, fillColor, fillOpacity);
			
			if(that.ispm.allowAreaEdit) {
				GEvent.addListener(poly, 'click', function(point) {
					if (that.ispm.mode == ISPM_MODE_FREE) {
						that.edit();
						that.ispm.callEvent('areaEdit', [that.id]);
					}
				});
			}
			
			ispm.map.addOverlay(poly);
			
			if(markers.length) {
				if(editable) {
					markers.each(function(marker) {
						marker.setImage(this.ispm.base_url + this.ispm.iconsPath + this.ispm.icons.editMarkerImg);
						marker.show();
					}, this);
					
					markers.getLast().setImage(this.ispm.base_url + this.ispm.iconsPath + this.ispm.icons.editMarkerActiveImg);
				}
				else {
					markers.each(function(marker) {
						marker.hide();
					});
				}
			}
		}
	},
	
	flush: function() {
		this.markers.each(function(marker, i) {
			marker.index = i;
		})
	},
	
	addMarker: function(point, index) {
		var marker = new GMarker(point, {
			icon: this.ispm.icons.editMarker,
			draggable: true,
			bouncy: true
		});
		
		marker.area = this;
		marker.selected = false;
		
		GEvent.addListener(marker, 'dragstart', function() {
			this.area.callEvent('dragStartMarker', [this]);
		});
		
		GEvent.addListener(marker, 'dragend', function() {
			this.area.callEvent('dragEndMarker', [this]);
			this.area.redraw();
		});
		
		this.ispm.map.addOverlay(marker);
		this.markers.splice(index !== undefined ? index : this.markers.length, 0, marker);
		
		this.flush();
		this.redraw();
		
		this.callEvent('addMarker', [marker]);
	},
	
	removeMarker: function(marker) {
		this.ispm.map.removeOverlay(marker)
		this.markers.remove(marker);
		
		this.flush();
		this.redraw();
		
		this.callEvent('removeMarker', [marker]);	
	},
	
	removeMarkers: function() {
		while(this.markers[0]) this.removeMarker(this.markers[0]);	
	},
	
	saveMarkers: function() {
		with(this) {
			savedMarkers.clear();
			
			markers.each(function(item) {
				savedMarkers.push(item.getLatLng());
			});
		}
	},
	
	revokeMarkers: function() {
		with(this) {
			if(!savedMarkers) return;
			
			this.removeMarkers();
			
			savedMarkers.each(function(item) {
				addMarker(item);
			});
			
			redraw();
		}
	},
	
	remove: function(fromServer) {
		var that = this;
		
		with(this) {
			free();
			
			removeMarkers();
			
			ispm.map.removeOverlay(poly);
			
			if(fromServer) {
				new mint.Request({
					url: ispm.base_url + "ispm_remote/remove_area",
					method: "POST",
					
					params: {
						area_id: that.id
					},
					
					events: {
						done: function() {
							that.callEvent('remove', [this.response]);
						}
					}
				})
			}
		}
	},
	
	getPoints: function() {
		var points = [];
		
		this.markers.each(function(marker) {
			points.push(marker.getLatLng());
		});
		
		return points;
	},
	
	getCenter: function() {
		return this.poly.getBounds().getCenter();
	}
});

var ISPM_AreaHistory = mint.Base.extend({
	area: null,
	
	actions: [],
	actionsPointer: 0,
	
	undoActionEl: 'ispm-area-history-undo-action',
	redoActionEl: 'ispm-area-history-redo-action',
	
	locked: false,
	
	__constructor: function(options) {
		var that = this;
		
		this.setOptions(options);
		
		this.area.addEvent('addMarker', function(marker) {
			that.add(ISPM_AREA_HISTORY_MARKER_ADD, {markerIndex: marker.index});
		});
		
		this.area.addEvent('removeMarker', function(marker) {
			that.add(ISPM_AREA_HISTORY_MARKER_REMOVE, {markerIndex: marker.index, markerPoint: marker.getLatLng()});
		});
		
		this.area.addEvent('dragStartMarker', function(marker) {
			that.add(ISPM_AREA_HISTORY_MARKER_DRAG, {markerIndex: marker.index, markerPoint: marker.getLatLng()});
		});
		
		this.area.addEvent('dragEndMarker', function() {	
		});
	},
	
	add: function(type, data) {
		if(this.locked) return;
		
		if(this.actionsPointer != this.actions.length-1) this.actions.clear();
		
		this.actions.push({
			type: type,
			data: data
		});
		
		this.actionsPointer = this.actions.length-1;
		
		this.update();
	},
	
	update: function() {
		var entry = this.actions[this.actionsPointer];
		
		if(entry) {
			switch(entry.type) {
				case ISPM_AREA_HISTORY_MARKER_ADD: {
					$(this.undoActionEl).innerHTML = "dodanie flagi";
					break;
				}
				case ISPM_AREA_HISTORY_MARKER_REMOVE: {
					$(this.undoActionEl).innerHTML = "usunięcie flagi";
					break;
				}
				case ISPM_AREA_HISTORY_MARKER_DRAG: {
					$(this.undoActionEl).innerHTML = "przesunięcie flagi";
					break;
				}
			}
		}
		else $(this.undoActionEl).innerHTML = "brak akcji";
	},
	
	undo: function() {
		var entry = this.actions[this.actionsPointer--];
		
		if(entry) {
			switch(entry.type) {
				case ISPM_AREA_HISTORY_MARKER_ADD: {
					this.lock();
					this.area.removeMarker(this.area.markers[entry.data.markerIndex]);
					this.unlock();
					break;
				}
				case ISPM_AREA_HISTORY_MARKER_REMOVE: {
					this.lock();
					this.area.addMarker(entry.data.markerPoint, entry.data.markerIndex);
					this.unlock();
					break;
				}
				case ISPM_AREA_HISTORY_MARKER_DRAG: {
					this.area.markers[entry.data.markerIndex].setLatLng(entry.data.markerPoint);
					this.area.redraw();
					break;
				}
			}
		}
		
		this.update();
	},
	
	redo: function() {
	},
	
	lock: function() {
		this.locked = true;
	},
	
	unlock: function() {
		this.locked = false;
	}
});

var ISPM_Point = mint.Base.extend({
	id: null,
	area: null,
	
	pos: null,
	point: null,
	
	__constructor: function(id, lat, lng, pos) {
		this.point = new GLatLng(lat, lng);
		this.pos = pos;
	}
});

var ISPM_Marker = mint.Base.extend({
	icon: null,
	marker: null,
	
	__constructor: function(options) {
		with(this) {
			setOptions(options);
			
			marker = new GMarker(null, {
				icon: icon,
				bounce: false,
				clickable: false,
				draggable: false
			})
		}
	},
	
	set: function(point) {
		this.marker.setLatLng(point);
	}
});

var ISPM_Widget = mint.Base.extend({
	ispm: null,
	
	__constructor: function(options) {
		this.setOptions(options);
	},
	
	disableBtn: function(btn) {
		with($(btn)) {
			onclick = function() {};
			setOpacity(50);
		}
	},
	
	enableBtn: function(btn) {
		with($(btn)) {
			setOpacity(__opacity);
		}
	},
	
	assignSubmit: function(el, fn) {
		$(el).addEvent("keydown", function(e) {
			if(e.keyCode == 13) fn();
		})
	}
});

var ISPM_AreaWidget = ISPM_Widget.extend({
	history: null,
	
	markerCounterEl: 'ispm-area-marker-counter',
	selectedCounterEl: 'ispm-area-selected-counter',
	
	__constructor: function(options) {
		var that = this;
		
		this.setOptions(options);
		
		this.history = new ISPM_AreaHistory({
			area: this.ispm.activeArea	
		});
		
		this.ispm.activeArea.addEvent('addMarker', function() {
			$(that.markerCounterEl).innerHTML = this.markers.length;
		});
		
		this.ispm.activeArea.addEvent('removeMarker', function() {
			$(that.markerCounterEl).innerHTML = this.markers.length;
		});
	},
	
	save: function() {
		var that = this;
		
		this.ispm.hideErrors();
		
		this.ispm.activeArea.name = mint.$('name-namearea').value;
		
		this.ispm.activeArea.save(true, function(error) {
			switch(error) {
				case ISPM_AREA_SAVED: {
					that.ispm.loadWidget('backend/network_info', {network_id: that.ispm.activeNetwork.id});
					break;
				}
				case ISPM_AREA_ERROR_NAME_EMPTY: {
					that.ispm.showError('name-empty');
					break;
				}
				case ISPM_AREA_ERROR_NAME_RESERVED: {
					that.ispm.showError('name-reserved');
					break;
				}
				case ISPM_AREA_ERROR_NAME_INVALID: {
					that.ispm.showError('name-invalid');
					break;
				}
				case ISPM_AREA_ERROR_NO_MARKERS: {
					that.ispm.showError('markers');
					break;
				}
				case ISPM_AREA_ERROR_OUTSIDE_COUNTRY: {
					that.ispm.showError('outside-country');
					break;
				}
				case ISPM_AREA_ERROR_TOO_BIG: {
					that.ispm.showError('too-big');
					break;
				}
			}
		});
		
		return false;
	},
	
	undo: function() {
		this.history.undo();
	},
	
	remove: function() {
		this.ispm.activeArea.remove(true);
		this.ispm.loadWidget('backend/network_info', {network_id: this.ispm.activeNetwork.id});
	},
	
	cancel: function() {
		this.ispm.activeArea.revokeMarkers();
		this.ispm.activeArea.cancel();
		this.ispm.loadWidget('backend/network_info', {network_id: this.ispm.activeNetwork.id});
	}
});