
//= depends: zapi/zapi
//= depends: zapi/google_maps/marker_light, zapi/google_maps/utils

ZAPI.namespace(window, 'APP.GoogleMaps').QuadTreeClusterer = Class.create({
	initialize: function(map, options, clusterMarkerOptions, singleMarkerOptions) {
		this._options = $H({
			'delay': 500,
			'enabled': true,
			'drawBBox': false
		}).merge(options);

		this._map = map;
		this._markers = [];
		this._bboxPolylines = [];
		this._enabled = this._options.get('enabled');
		this._totalPoints = 0;
		this._fade = $H();

		if (this.handlers) {
			$H(this.handlers).each(function(handler) {
				this.handlers[handler.key] = handler.value.bind(this);
			}.bind(this));
		}
		else {
			this.handlers = {};
		}
	},

	_drawBBox: function(bbox) {
		var polyline = new google.maps.Polyline([
			new google.maps.LatLng(bbox.n, bbox.w),
			new google.maps.LatLng(bbox.n, bbox.e),
			new google.maps.LatLng(bbox.s, bbox.e),
			new google.maps.LatLng(bbox.s, bbox.w),
			new google.maps.LatLng(bbox.n, bbox.w)
		]);
		this._bboxPolylines.push(polyline);
		this._map.addOverlay(polyline);
	},

	_createClusterMarker: function(cluster) {
		var zoom = this._map.getZoom(),
		    marker;

		if (this.handlers.createClusterMarker) {
			marker = this.handlers.createClusterMarker(cluster);
		}
		else {
			marker = new APP.GoogleMaps.ClusterMarker(this._map, cluster);
		}

		marker._zapiData = cluster;
		this._map.addOverlay(marker);
		this._markers.push(marker);

		if (this._options.get('drawBBox') && cluster.bbox) {
			this._drawBBox(cluster.bbox);
		}

		if (cluster.bbox) {
			(function() {
				var bbox = cluster.bbox,
					sw = new google.maps.LatLng(bbox.s, bbox.w),
					ne = new google.maps.LatLng(bbox.n, bbox.e),
					bounds = new google.maps.LatLngBounds(sw, ne),
					boundsCenter = bounds.getCenter(),
					boundsZoom = this._map.getBoundsZoomLevel(bounds);
				marker._zapiData.bounds = bounds;

				if (this.handlers.clusterMarkerAdded) {
					this.handlers.clusterMarkerAdded(marker);
				}

				if (!Prototype.Browser.IE) {
					Event.observe(marker.getContainer(), 'mouseenter', function() {
						this.highlightMarker(marker);
					}.bind(this));

					Event.observe(marker.getContainer(), 'mouseleave', function() {
						this.unhighlightMarker(marker);
					}.bind(this));
				}
			}.bind(this)());
		}
	},

	_createSingleMarker: function(listing) {
		var marker;

		if (this.handlers.createSingleMarker) {
			marker = this.handlers.createSingleMarker(listing);
		}
		else {
			marker = new APP.GoogleMaps.ListingMarker(
				new google.maps.LatLng(listing.features.lat, listing.features.lng),
				listing.name
			);
		}

		marker._zapiData = listing;
		this._map.addOverlay(marker);
		this._markers.push(marker);

		if (this.handlers.singleMarkerAdded) {
			this.handlers.singleMarkerAdded(marker);
		}

		if (!Prototype.Browser.IE) {
			Event.observe(marker.getContainer(), 'mouseenter', function() {
				this.highlightMarker(marker);
			}.bind(this));

			Event.observe(marker.getContainer(), 'mouseleave', function() {
				this.unhighlightMarker(marker);
			}.bind(this));
		}
	},

	enable: function() {
		this._enabled = true;
	},

	disable: function() {
		this._enabled = false;
	},

	getTotalPoints: function() {
		return this._totalPoints;
	},

	getJSON: function() {
		return this._json;
	},

	clearMarkers: function() {
		this.resetHighlight();
		this._markers.each(function(m) {
			m.remove();
		}.bind(this));
		this._markers = [];
	},

	clearBBoxPolylines: function() {
		this._bboxPolylines.each(function(p) {
			this._map.removeOverlay(p);
		}.bind(this));
		this._bboxPolylines = [];
	},

	_moveMarkerToFloatPane: function(marker) {
		this._map.getPane(google.maps.MAP_FLOAT_SHADOW_PANE).appendChild(
			(function() {
				var el = marker.getContainer();
				if (el.parentNode) {
					Element.remove(el);
				}
				return el;
			}())
		);
	},

	_moveMarkerToMarkerPane: function(marker) {
		this._map.getPane(google.maps.MAP_MARKER_PANE).appendChild(
			(function() {
				var el = marker.getContainer();
				if (el.parentNode) {
					Element.remove(el);
				}
				return el;
			}())
		);
	},

	resetHighlight: function() {
		if (this._highlighted) {
			this._moveMarkerToMarkerPane(this._highlighted);
			this._highlighted.unhighlight();
			this._highlighted = null;
			this._fade = $H();
		}
		Element.setOpacity(this._map.getPane(google.maps.MAP_MARKER_PANE), 1);
		Element.setOpacity(this._map.getPane(google.maps.MAP_MARKER_SHADOW_PANE), 1);
	},

	highlightMarker: function(marker) {
		if (this._highlighted) {
			if (this._highlighted != marker) {
				this._highlighted.unhighlight();
				this._moveMarkerToMarkerPane(this._highlighted);
			}
		}

		marker.highlight();
		this._highlighted = marker;
		this._moveMarkerToFloatPane(marker);

		if (this._fade.keys().length > 0) {
			this._fade.each(function(pair) { pair.value.cancel(); });
		}

		if (!Prototype.Browser.IE) {
			$A([
				google.maps.MAP_MARKER_PANE,
				google.maps.MAP_MARKER_SHADOW_PANE
			]).each(function(pane) {
				this._fade.set(pane, Effect.Fade(
					this._map.getPane(pane), {
						'to':       0.5,
						'duration': 0.3,
						'afterFinish': function() {
							this._fade.unset(pane);
						}.bind(this)
					}
				));
			}.bind(this));
		}
	},

	unhighlightMarker: function(marker) {
		if (this._fade.keys().length > 0) {
			this._fade.each(function(pair) { pair.value.cancel(); });
		}

		if (!Prototype.Browser.IE) {
			$A([
				google.maps.MAP_MARKER_PANE,
				google.maps.MAP_MARKER_SHADOW_PANE
			]).each(function(pane) {
				this._fade.set(pane, Effect.Appear(
					this._map.getPane(pane), {
						'to':       1.0,
						'duration': 0.3,
						'afterFinish': function() {
							this._fade.unset(pane);
							this._moveMarkerToMarkerPane(marker);
							marker.unhighlight();
						}.bind(this)
					}
				));
			}.bind(this));
		}
	},

	findMarkerByQuadTree: function(quadTree) {
		if (this._markers.length <= 0) {
			return;
		}
		else {
			return this._markers.detect(function(m) {
				return quadTree.indexOf(m._zapiData.quadTree) === 0;
			});
		}
	},

	reloadMarkers: function(force) {
		if (!this._enabled) {
			return;
		}

		if (this.handlers.beforeClustererRequest) {
			this.handlers.beforeClustererRequest();
		}

		var req = new Ajax.Request(
			this.handlers.clustererRequest(), {
				'method': 'get',
				'onSuccess': function(response) {
					function _extractFolder(json, folder) {
						if (json.folders) {
							return json.folders.detect(function(f) { return f.id == folder; });
						}
						else {
							return [];
						}
					}

					var json = Object.traverse(
							response.responseJSON, $w('ZAPI Listings Clusterer document')
						),
						clusters = _extractFolder(json, 'clusters'),
						singles = _extractFolder(json, 'listings');
					this._json = json;

					this.clearMarkers();
					this.clearBBoxPolylines();
					this._totalPoints = Number.interpret(json.totalPoints);

					clusters.placemarks.each(this._createClusterMarker.bind(this));
					singles.placemarks.each(this._createSingleMarker.bind(this));

					if (this.handlers.afterClustererRequest) {
						this.handlers.afterClustererRequest();
					}
				}.bind(this)
			}
		);
	}
});
