var Map = {

	DISPATCHERS: {
		save: 'index.php?do=saveMapObject', 
		fetch: 'index.php?do=fetchMapObjects',
		info: 'index.php?do=mapObjectInfo',
		search: 'index.php?do=mapSearch'
	},
	CANVAS: { element: 'map_canvas', width: '570', height: '320' },
	VIEW: { latlng: new GLatLng(49.59647007089266, 21.082763671875), zoom: 7  },
	
	createIcons: function(images) {
		var defaultIcon = new GIcon();
		defaultIcon.shadow = 'http://labs.google.com/ridefinder/images/mm_20_shadow.png';
		defaultIcon.iconSize = new GSize(32, 32);
		defaultIcon.shadowSize = new GSize(22, 20);
		defaultIcon.iconAnchor = new GPoint(6, 20);
		defaultIcon.infoWindowAnchor = new GPoint(5, 1);
		var icons = {};
		$H(images).each(function(entry) {
			var typeId = entry.key, typeImage = entry.value;
			icons[typeId] = new GIcon(defaultIcon, typeImage);
		});
		
		this.defaultIcon = defaultIcon;
		this.objectsIcons = icons;
	},
	
	load: function(objects, icons, msgs) {
		this.msgs = msgs || {};
		if (GBrowserIsCompatible()) {
			this.createMap(this.CANVAS);
			this.setView(this.VIEW)
			this.createIcons(icons);
			this.objects = objects;
			this.markObjects(objects);
   	} else {
   		this.error(this.msgs.incompBrowser);
   		return false;
   	} 
	},
	
	createMap: function(canvas) {
		var c = $(canvas.element);
		c.setStyle({ width: canvas.width+'px', height: canvas.height+'px' });
		this.map = new GMap2(c);
		this.geocoder = new GClientGeocoder();
		this.map.addControl(new GLargeMapControl());
	},
	
	setView: function(params) {
		this.map.setCenter(this.VIEW.latlng, this.VIEW.zoom);
	},
	
	markObjects: function(objects) {
	  var empty = function(objects) {
      //if (Object.isArray(objects)) return objects.size() == 0;
      return Object.values(objects).size() == 0;
    };
    if (empty(objects)) {
      return;
    }
		var self = this;
		$H(objects).each(function(entry) {
			self.markObject(entry.value);
		});
	},
	
	createMarker: function(latlng, look) {
		var marker = new GMarker(new GLatLng(latlng.lat, latlng.lng),
			{ bouncy: false, draggable: true, icon: this.objectsIcons[look.type] }
		);
		if (look.editable) {
			this.enableMarkerEditing(marker);
		} else {
			this.disableMarkerEditing(marker);
		}
		this.map.addOverlay(marker);
		return marker;
	},
	
	markObject: function(object, editable, asNew) {
		if (this.objects[object.id] && this.objects[object.id].marker && !this.objects[object.id].marker.isHidden()) return;
		if (!this.objects[object.id]) this.objects[object.id] = object;
		if (this.objects[object.id].marker && this.objects[object.id].marker.isHidden()) {
			this.objects[object.id].marker.show();
			return;
		}
		var marker = this.createMarker({ lat: object.lat, lng: object.lng }, { type: object.type, editable: editable });
		if (asNew)
			this.newMarker = marker;
		
		if (editable) {
			var self = this;
			GEvent.addListener(marker, 'dragend', function() { 
				self.updateLocation(marker.getLatLng());
				$('object_lat').value = marker.getLatLng().lat();
	      $('object_lng').value = marker.getLatLng().lng();
			});
		}	else {
      this.attachInfoWindow(marker, object.id);
		}
    this.objects[object.id].marker = marker;
    // marker ma GLatLng, wiec pola lat, lng nie sa potrzebne
    if (this.objects[object.id].lat) delete this.objects[object.id].lat;
    if (this.objects[object.id].lng) delete this.objects[object.id].lng;
	},
	
	attachInfoWindow: function(marker, objectId) {
    var self = this;
    GEvent.addListener(marker, 'click', function() { self.showObjectInfo(marker, objectId); });
	},
	
	showObjectInfo: function(marker, objectId) {
		if (this.objects[objectId].info) { //TODO wygasanie cache'a
		 	var content = new Element('div', { id: 'mapInfoWindow' }).update(this.objects[objectId].info);
		 	marker.openInfoWindow(content);
		} else {
			var content = new Element('div', { id: 'mapInfoWindow' }).update(this.msgs.working);
	  	marker.openInfoWindow(content);
	  	var self = this;
		 	new Ajax.Request(this.DISPATCHERS.info, {
				method: 'get',
				parameters: { id: objectId },
				onComplete: function(transport) {
					self.objects[objectId].info = transport.responseText;
				  content.update(transport.responseText);
				 },
				 onFailure: function() {
					content.update(self.msgs.cantConnect);
				}
			});
		}
	},
	
	markLocation: function(location, objectType, after) {
		var self = this;
		this.findLatLng(location, function(latlng) {
			self.map.setCenter(latlng, self.map.getZoom());
			if (!self.newMarker) {
				self.newMarker = self.createMarker({ lat: latlng.lat(), lng: latlng.lng() }, { type: objectType, editable: true });
				GEvent.addListener(self.newMarker, 'dragend', function() { 
					self.updateLocation(self.newMarker.getLatLng());
					 $('object_lat').value = self.newMarker.getLatLng().lat();
			     $('object_lng').value = self.newMarker.getLatLng().lng();
				});
			} else {
				self.newMarker.setLatLng(latlng);
			}
			//self.updateLocation(latlng);
			$('object_location').value = location;
      if (after) after();
		});
	},

	locateObject: function(object) {
		this.map.setCenter(new GLatLng(object.lat, object.lng), this.map.getZoom());
    if (!this.objects[object.id]) {
    	var marker = this.createMarker({ lat: object.lat, lng: object.lng }, { type: object.type, editable: false });
        this.attachInfoWindow(marker, object.id);
        this.objects[object.id] = { marker: marker, id: object.id, type: object.type };
      }
      this.showObjectInfo(this.objects[object.id].marker, object.id);
	},
	
	findLatLng: function(location, whenFound) {
		if (!location) {
			whenFound(this.VIEW.latlng);
			return;
		}
		if (this.geocoder) {
			var self = this;
			this.geocoder.getLatLng(location, function(latlng) {
				if (!latlng) {
					alert(self.msgs.cantLocate);
				} else {
					whenFound(latlng);
				}
			});
		}
	},
	
	updateLocation: function(latlng) {
		var self = this;
		if (latlng) {
			this.geocoder.getLocations(latlng, function(response) {
				if (!response || response.Status.code != 200) {
					self.flash({ error: self.msgs.cantConnect });
				} else {
					var location = response.Placemark[0];
					$('object_location').value = location.address;
				}
			});
		}
	},	
	
	saveChanges: function() {
	    var self = this;
	    var validatePresenceOf = function(required) {
	      var valid = true;
	      required.each(function(field) {
	        if (!$(field).present()) {
	          valid = false;
	          return;
	        }
	      });
	      if (!valid) {
	        alert(self.msgs.invalidFields);
	        return false;
	      }
	      return true;
	    };
	    if (!validatePresenceOf(['object_name', 'object_location']))
	    	return false;
	
	    var sendRequest = function() {
	      new Ajax.Request(self.DISPATCHERS.save, {
	        method: 'post',
	        parameters: $('add_object_form').serialize(),
	        onSuccess: function(transport) {
	          var response = transport.responseText.stripTags().evalJSON(true);
	          self.objects[response.object.id] = response.object;
	          self.attachInfoWindow(self.newMarker, response.object.id);
	          self.disableMarkerEditing(self.newMarker);
	          self.objects[response.object.id].marker = self.newMarker;
	          self.newMarker = null;
	          self.dismissForm('add_object_form');
	          self.flash(response.status);
	        },
	        onFailure: function() {
	          //self.flash({ error: self.msgs.cantConnect });
	        }
	      });
	    };
	
	    //TODO spr lokalizacji tylko jesli nie zmienila sie od ostatniego `Ustaw na mapie`
	    this.markLocation($('object_location').getValue(), $('object_type').getValue(), function() {
	      $('object_lat').value = self.newMarker.getLatLng().lat();
	      $('object_lng').value = self.newMarker.getLatLng().lng();
	      sendRequest();
	    });
	},
	
	search: function(query, advanced, page) {
		this.flash({ ok: this.msgs.working });
		var self = this; 

	    var sendRequest = function(form) {
	      new Ajax.Updater('search_results', 
	    	self.DISPATCHERS.search + (form === 'adv_map_search' ? '&adv=1' : '') + (page != undefined ? '&page=' + page : ''), {
				  method: 'get',
				  parameters: $(form).serialize(),
				  
				  onComplete: function() {
				  	$('map_flash').update();
				  	self.hideObjects();
				  	
				  	if ($('not_found')) {
				  		self.setView(self.VIEW);
				  		return;
				  	}
						$('search_results').select('li').each(function(item) {
							var object = {
								id: item.select('input.object_id').first().getValue(),
								type: item.select('input.object_type').first().getValue(),
								lat: item.select('input.object_lat').first().getValue(),
								lng: item.select('input.object_lng').first().getValue()
							};
							//alert(Object.toJSON(object));
							self.markObject(object, false);
						});			  	
					  var foundArea = new GLatLngBounds(
					  	new GLatLng($('sw_lat').getValue(), $('sw_lng').getValue()),
					  	new GLatLng($('ne_lat').getValue(), $('ne_lng').getValue())
					  );
					  self.map.setCenter(foundArea.getCenter(), self.map.getBoundsZoomLevel(foundArea));
				  },
				  
				  onFailure: function() {
					  self.flash({ error: self.msgs.cantConnect });
				  }
			  });
	    };
	
	    var advLocation = $('adv_location').getValue().strip();
	    var shouldLocate = !advanced || !advLocation.blank();
	    if (shouldLocate) {
	      if (this.geocoder) {
	        var useAdvLocation = advanced && !advLocation.blank();
	        this.geocoder.getLatLng(useAdvLocation ? advLocation : query, function(latlng) {
	          if (latlng) {
	            $(useAdvLocation ? 'adv_search_lat' : 'search_lat').value = latlng.lat();
	            $(useAdvLocation ? 'adv_search_lng' : 'search_lng').value = latlng.lng();
	          } else {        
	            $('search_lat').value = $('search_lng').value = $('adv_search_lat').value = $('adv_search_lng').value = '';
	            if (useAdvLocation) { //TODO szukanie zaawans. po lokalizacji ale google nie znalazl wsp => koniec
	              alert(self.msgs.cantLocate);
	              return false;
	            }
	          }
	          sendRequest(useAdvLocation ? 'adv_map_search' : 'search_map');
			});
	      } //TODO nie ma geocodera!
	
	    } else { // szukanie zaawans ale bez lokalizacji
	      $('adv_search_lat').value = $('adv_search_lng').value = '';	
	      sendRequest('adv_map_search');
	    }
	},
	
	hideObjects: function() {
		$H(this.objects).each(function(object) {
			if (object.value.marker) object.value.marker.hide();
		});
	},

  //TODO potwierdzenie tylko gdy wprow jakies wart w form
  cancelChanges: function() {
    if (confirm(this.msgs.cancelConfirm)) {
	  	if (this.newMarker) {
		  	this.map.removeOverlay(this.newMarker);
		  	this.newMarker = null;
		  }
      this.dismissForm('add_object_form');
    }
	},
	
	enableMarkerEditing: function(marker) {
		marker.enableDragging();
	},
	
	disableMarkerEditing: function(marker) {
		marker.disableDragging();
	},
	
	flash: function(msg) {
		$('map_flash').update('<span>'+ (msg.error || msg.ok || msg) + '</span>');
		$$('#map_flash span').first().addClassName(msg.error ? 'error' : 'notice'); 
	},

  dismissForm: function(form) {
    $(form).hide();
    $(form).reset();
  },
	
	destroy: function() { GUnload(); }
};

