Ext.define("Ext.ux.GMapPanel", {
	extend: "Ext.panel.Panel",

	alias: "widget.gmappanel",

	config: {
		center: null,
		mapOption: {},
		scaleControl: true,
		geoCoder: null,
		map: null,
		markers: [],
		zoom: 7,
		stored: null,
		added: [],
		api: "//maps.googleapis.com/maps/api/js",
		gestureHandling: "greedy"
	},

	border: false,

	listeners: {
		afterrender: {
			fn: function(i_panel) {

				var i_self = this;

				var o_google = window.google || {};

				if (o_google.maps) {
					i_self.initMap();
				}
				else {
					var i_entry = Ext.Boot.getEntry(i_self.getApi(), {
						crossDomain: true,
						listeners: [
							function(i_entry) {
								o_google = window.google || {};
								if (o_google.maps) {
									i_self.initMap();
								}
								else {
									Ext.raise("Error loading google maps api at :" + i_self.getApi());
								}
							}
						]
					});
					i_entry.load(false);
				}
			},
			buffer: 10
		},
		beforedestroy: {
			fn: function(i_panel) {

				var i_self = this;
				var a_markers = i_self.getAdded();

				Ext.Array.each(a_markers, function(i_marker) {
					i_marker.setMap(null);
				});

				i_self.setAdded(null);
				i_self.setMap(null);
				i_self.setStored(null);
				i_self.setCenter(null);
				i_self.setGeoCoder(null);
			}
		}
	},

	initComponent: function() {

		var i_self = this;
		i_self.callParent();
	},

	initMap: function() {

		var i_self = this;

		var o_center = i_self.getCenter() || {
			lat: 55.846330,
			lng: 10.593858
		};

		var o_stored = i_self.getStored() || "";

		if (o_stored) {
			if (o_stored.lat && o_stored.lng) {
				o_center = o_stored;
			}
		}

		if (o_center) {
			if (o_center.geoCodeAddr) {
				i_self.lookupCode(o_center.geoCodeAddr, o_center.marker);
			}
			else {
				i_self.createMap(o_center);
			}
		}
		else {
			Ext.raise("center is required");
		}
	},

	createMap: function(o_center, o_marker) {

		var i_self = this;
		var a_markers = i_self.getMarkers();
		var google = window.google;

		var o_options = Ext.apply({}, i_self.mapOptions);

		o_options = Ext.applyIf(o_options, {
			zoom: i_self.getZoom(),
			center: o_center,
			mapTypeId: google.maps.MapTypeId.MAP,
			scaleControl: i_self.getScaleControl(),
			gestureHandling: i_self.getGestureHandling()
		});

		var i_map = new google.maps.Map(i_self.body.dom, o_options);

		i_self.setMap(i_map);

		if (o_marker) {
			i_self.addMarker(Ext.applyIf(o_marker, {
				position: o_center
			}));
		}

		Ext.Array.each(a_markers, i_self.addMarker, i_self);

		i_self.setCenter(o_center);

		i_self.fireEvent("mapready", i_self, i_map);
	},

	addMarker: function(o_config) {

		var i_self = this;
		var google = window.google;

		o_config = Ext.apply({
			map: i_self.getMap()
		}, o_config);

		if (!o_config.position) {
			o_config.position = new google.maps.LatLng(o_config.lat, o_config.lng);
		}
		var i_marker = new google.maps.Marker(o_config);

		Ext.Object.each(i_marker.listeners, function(s_name, f_handler) {
			google.maps.event.addListener(i_marker, s_name, f_handler);
		});

		var a_added = i_self.getAdded();
		a_added.push(i_marker);

		return i_marker;
	},

	removeMarker: function(i_marker) {

		var i_self = this;
		var a_added = i_self.getAdded();

		Ext.Array.remove(a_added, i_marker);

		i_marker.setMap(null);
	},

	lookupCode: function(addr, marker) {

		var i_self = this;
		var i_geocoder = i_self.getGeoCoder();
		var google = window.google;

		if (!i_geocoder) {
			i_geocoder = new google.maps.Geocoder();
			i_self.setGeoCoder(i_geocoder);
		}

		i_geocoder.geocode({
			address: addr
		}, Ext.Function.bind(i_self.onLookupComplete, i_self, [marker], true));
	},

	lookupAddr: function(addr, f_callback) {

		var i_self = this;

		f_callback = f_callback || function(o_data) {
			var i_element = i_self.getEl();
			if (i_element && i_element.dom) {
				if (i_self.getMap()) {
					var i_map = i_self.getMap();
					if (o_data[0]) {
						i_map.setCenter(o_data[0].geometry.location);

						var a_markers = i_self.getAdded();

						Ext.Array.each(a_markers, function(i_marker) {
							i_marker.setMap(null);
						});

						i_self.setAdded([]);

						i_self.addMarker({
							position: o_data[0].geometry.location
						});
					}
				}
				else {
					if (o_data[0]) {
						i_self.setStored(o_data[0].geometry.location);
					}
				}
			}
			else {
				if (o_data[0]) {
					i_self.setStored(o_data[0].geometry.location);
				}
			}
		};

		var i_geocoder = i_self.getGeoCoder();
		var google = window.google;

		if (!i_geocoder) {
			i_geocoder = new google.maps.Geocoder();
			i_self.setGeoCoder(i_geocoder);
		}

		i_geocoder.geocode({
			address: addr
		}, Ext.Function.bind(f_callback, i_self, [{}], true));
	},

	onLookupComplete: function(o_data, response, marker) {

		var i_self = this;

		if (response != "OK") {
			Ext.MessageBox.alert("Error", "An error occured: \"" + response + "\"");
			return;
		}
		var i_element = i_self.getEl();
		if (i_element.dom) {
			i_self.createMap(o_data[0].geometry.location, marker);
		}
		else {
			i_self.setStored(o_data[0].geometry.location);
		}
	},

	afterComponentLayout: function(w, h) {

		var i_self = this;

		i_self.callParent(arguments);
		i_self.redraw();
	},

	redraw: function() {

		var i_self = this;
		var google = window.google;
		var i_map = i_self.getMap();

		if (i_map) {
			google.maps.event.trigger(i_map, "resize");
		}
	}

});
