// POI-Manager
// Ist fuer die Darstellung der ausgewaehlten POIs zustaendig. Es werden nur
// jene POIs auf der Karte dargestellt, die im aktuellen Viewport auch sichtbar
// sind.
function PoiManager(map) {
	this.map = map;
	this.bounds = this.map.getBounds();
	this.zoom = this.map.getZoom();
	this.itemList = new Array();
	this.poiList = new Object();

	this.visPoiList = new Object();

	this.bunchItemList = new Array();
	this.bunchList = new Object();

	var self = this;

	GEvent.addListener(this.map, 'moveend', function() {
		self._refresh();
	});

	//GLog.write('PoiManager initialisiert');
}

PoiManager.prototype.addPois = function(pois, item) {
	this._checkItem(item);

	var tmpPoiList = new Array();

	var marker = null;
	for(var i = 0; i < pois.length; i++)
		this.poiList[item].push(pois[i]);

	for(var i = 0; i < this.poiList[item].length; i++) {
		// Den POI nur dann in die visList aufnehmen, falls dieser im
		// sichtbaren Bereich liegt
		if(this.bounds.containsLatLng(this.poiList[item][i].point))
			tmpPoiList.push(this.poiList[item][i]);
	}

	if(tmpPoiList.length != 0) {
		for(var i = 0; i < this.visPoiList[item].length; i++)
			this.map.removeOverlay(this.visPoiList[item][i].usedMarker);

		this.visPoiList[item] = this.clusterize(tmpPoiList);

		for(var i = 0; i < this.visPoiList[item].length; i++) {
			marker = this.visPoiList[item][i].getMarker(this.zoom);
			this.map.addOverlay(marker);
			this.visPoiList[item][i].usedMarker = marker;
		}
	}

	delete tmpPoiList;

	//GLog.write(item + ': ' + pois.length + ' POIs hinzugefuegt');

	return;
}

PoiManager.prototype.addPoi = function(poi, item) {
	this._checkItem(item);

	var tmpPoiList = new Array();

	this.poiList[item].push(poi);

	for(var i = 0; i < this.poiList[item].length; i++) {
		// Den POI nur dann in die visList aufnehmen, falls dieser im
		// sichtbaren Bereich liegt
		if(this.bounds.containsLatLng(this.poiList[item][i].point))
			tmpPoiList.push(this.poiList[item][i]);
	}

	var marker = null;
	if(tmpPoiList.length != 0) {
		for(var i = 0; i < this.visPoiList[item].length; i++)
			this.map.removeOverlay(this.visPoiList[item][i].usedMarker);

		this.visPoiList[item] = this.clusterize(tmpPoiList);

		for(var i = 0; i < this.visPoiList[item].length; i++) {
			marker = this.visPoiList[item][i].getMarker(this.zoom);
			this.map.addOverlay(marker);
			this.visPoiList[item][i].usedMarker = marker;
		}
	}

	delete tmpPoiList;

	//GLog.write(item + ': 1 POI hinzugefuegt');

	return;
}

PoiManager.prototype._checkItem = function(item) {
	// Schauen, ob es dieses Item schon in der Liste hat. Wenn nicht, alles
	// so vorbereiten, als waere es schon immer da gewesen.
	if(this.itemList.indexOf(item) == -1) {
		this.itemList.push(item);
		this.poiList[item] = new Array();
		this.visPoiList[item] = new Array();

		//GLog.write('Neuer Eintrag in der itemList: ' + item);
	}

	return;
}

PoiManager.prototype.removePois = function(item) {
	var i = this.itemList.indexOf(item);

	//GLog.write('removePois: ' + item);

	if(i == -1)
		return;

	//GLog.write('Eintrag aus der itemList entfernen: ' + item);

	this.itemList.splice(i, 1);

	for(var i = 0; i < this.visPoiList[item].length; i++) {
		this.map.removeOverlay(this.visPoiList[item][i].usedMarker);
		this.visPoiList[item][i].usedMarker = null;
	}

	this.clearCluster(item);

	delete this.poiList[item];
	delete this.visPoiList[item];

	return;
}

// Damit kann der User einen kompletten Refresh der POIs erzwingen
PoiManager.prototype.refresh = function(item) {
	this._refresh(true, item);

	return;
}

PoiManager.prototype._refresh = function(force, item) {
	force = force || false;
	var zoomed = false;

	//GLog.write('refresh');

	// Falls gezoomed wurde, muessen wir evtl. ein paar Marker neu laden
	if(this.map.getZoom() != this.zoom) {
		//force = true;
		zoomed = true;
		this.zoom = this.map.getZoom();
	}

	var bounds = this.map.getBounds();

	// Hier hat sich nicht geaendert
	if(this.bounds.equals(bounds) && force == false)
		return;

	// Hier muessen wir zuerst alle sichtbaren POIs entfernen
	if(force == true) {
		//GLog.write('forced refresh: alle visPOIs entfernen');

		for(var i in this.visPoiList) {
			for(var j = 0; j < this.visPoiList[i].length; j++) {
				this.map.removeOverlay(this.visPoiList[i][j].usedMarker);
				this.visPoiList[i][j].usedMarker = null;
			}
		}

		this.visPoiList = new Object();

		for(var i = 0; i < this.itemList.length; i++)
			this.visPoiList[this.itemList[i]] = new Array();

		this.clearClusters();
	}

	this.bounds = bounds;

	var tmpPoiList = new Object();
	var oldPoiList = new Object();
	var newPoiList = new Object();

	var item = '';
	var p = null;
	var marker = null;

	for(var i = 0; i < this.itemList.length; i++) {
		item = this.itemList[i];

		tmpPoiList[item] = new Array();
		oldPoiList[item] = new Array();
		newPoiList[item] = new Array();

		// Alle POIs durchgehen und bestimmen, welche sichtbar sind
		for(var j = 0; j < this.poiList[item].length; j++) {
			p = this.poiList[item][j];

			if(this.bounds.contains(p.point))
				tmpPoiList[item].push(p);
		}

		// Clustering
		tmpPoiList[item] = this.clusterize(tmpPoiList[item]);

		// Alle POIs bestimmen, welche neu sind, und welche alt sind
		if(force == false) {
			for(var j = 0; j < tmpPoiList[item].length; j++) {
				p = tmpPoiList[item][j];

				if(this.visPoiList[item].indexOf(p) == -1)
					newPoiList[item].push(p);
			}

			for(var j = 0; j < this.visPoiList[item].length; j++) {
				p = this.visPoiList[item][j];

				if(tmpPoiList[item].indexOf(p) == -1)
					oldPoiList[item].push(p);
			}
		}
		else
			newPoiList[item] = tmpPoiList[item];

		//GLog.write(item + ': ' + oldPoiList[item].length + ' visPOIs entfernen');
		//GLog.write(item + ': ' + newPoiList[item].length + ' visPOIs hinzufuegen');

		// Alle nicht mehr sichtbaren POIs entfernen
		for(var j = 0; j < oldPoiList[item].length; j++) {
			this.map.removeOverlay(oldPoiList[item][j].usedMarker);
			oldPoiList[item][j].usedMarker = null;
		}

		// Alle neu sichtbaren POIs anzeigen
		for(var j = 0; j < newPoiList[item].length; j++) {
			marker = newPoiList[item][j].getMarker(this.zoom);
			this.map.addOverlay(marker);
			newPoiList[item][j].usedMarker = marker;
		}

		// Wenn gezoomed wurde, kann es sein, dass sich der Marker geaendert hat,
		// daher gehen wir nochmals alle sichtbaren Marker durch, um zu schauen, ob
		// das der Fall ist. Wenn ja, den alten Marker entfernen und den neuen
		// auf die Karte setzen.
		if(zoomed == true) {
			for(var j = 0; j < tmpPoiList[item].length; j++) {
				marker = tmpPoiList[item][j].getMarker(this.zoom);
				if(marker != tmpPoiList[item][j].usedMarker) {
					//GLog.write('marker austauschen');

					this.map.removeOverlay(tmpPoiList[item][j].usedMarker);
					this.map.addOverlay(marker);
					tmpPoiList[item][j].usedMarker = marker;
				}
			}
		}

		//GLog.write(item + ': ' + tmpPoiList[item].length + ' visPOIs insgesamt');
	}

	this.visPoiList = tmpPoiList;

	delete tmpPoiList;
	delete oldPoiList;
	delete newPoiList;

	return;
}

PoiManager.prototype.clusterize = function(pois) {
	var bunch = null;
	var poi = null;
	var ne = null;
	var sw = null;

	var length = pois.length;

	if(length <= 150)
		return pois;

	//GLog.write('clusterize: ' + pois.length + ' pois');

	var item = pois[0].item;
	this._checkBunchItem(item);

	// 1. Alle sichtbaren POIs durchgehen und schauen, ob sie in ein
	//    schon bestehenden Bunch passen, sofern nicht schon in einem Bunch

	var poi = null;
	for(var c = 0; c < pois.length; c++) {
		poi = pois[c];

		if(poi.inCluster)
			continue;

		for(var i = 0; i < this.bunchList[item].length; i++) {
			bunch = this.bunchList[item][i];
			// Abstand berechnen
			if(bunch.bounds.contains(poi.point)) {
				bunch.push(poi);
				break;
			}
		}

		// 1.1. Falls dieser POI in keinem Bunch ist, wird ein Bunch mit
		//      diesem POI erstellt

		if(!poi.inCluster) {
			point = _map.fromLatLngToDivPixel(poi.point);

			ne = _map.fromDivPixelToLatLng(new GPoint(point.x + 10, point.y - 10));
			sw = _map.fromDivPixelToLatLng(new GPoint(point.x - 10, point.y + 10));

			bunch = new ClusterPoi(poi, new GLatLngBounds(sw, ne));
			bunch.push(poi);

			this.bunchList[item].push(bunch);
		}
	}

	// 2. Die sichtbaren Cluster als Liste zurueckgeben

	var new_pois = new Array();

	for(var i = 0; i < this.bunchList[item].length; i++) {
		bunch = this.bunchList[item][i];

		if(bunch.poiList.length == 0)
			continue;

		if(this.bounds.contains(bunch.point)) {
			if(bunch.poiList.length == 1) {
				poi = bunch.pop();
				bunch.clear();
				new_pois.push(poi);
			}
			else
				new_pois.push(bunch);
		}
	}

	//GLog.write('bunches: ' + new_pois.length);

	return new_pois;
}

PoiManager.prototype.clearCluster = function(item) {
	var i = this.bunchItemList.indexOf(item);
	if(i == -1)
		return;

	for(var j = 0; j < this.bunchList[item].length; j++)
		this.bunchList[item][j].clear();

	delete this.bunchList[item];
	this.bunchItemList.splice(i, 1);
}

PoiManager.prototype.clearClusters = function() {
	for(var i in this.bunchList) {
		for(var j = 0; j < this.bunchList[i].length; j++)
			this.bunchList[i][j].clear();
	}

	this.bunchList = new Object();
	this.bunchItemList = new Array();
}

PoiManager.prototype._checkBunchItem = function(item) {
	// Schauen, ob es dieses Item schon in der Liste hat. Wenn nicht, alles
	// so vorbereiten, als waere es schon immer da gewesen.
	if(this.bunchItemList.indexOf(item) == -1) {
		//GLog.write('new bunchItemList');
		this.bunchItemList.push(item);
		this.bunchList[item] = new Array();
	}

	return;
}

PoiManager.prototype.getPoiCount = function(item) {
	if(this.itemList.indexOf(item) == -1)
		return 0;

	return this.poiList[item].length;
}

PoiManager.prototype.getVisPoiCount = function(item) {
	if(this.itemList.indexOf(item) == -1)
		return 0;

	return this.visPoiList[item].length;
}

