﻿general.namespace('SW.Markers');

(function () {

        var Markers = SW.Markers;

    	/* PUBLIC constructor
		 */
		var MarkerGrid = Markers.MarkerGrid = function () {
			// setup hashes
			var hashes = [
				'aquiredSectorsByType',
				'sectorIdsPendingRequestByType',
				'displayedMarkersByType'
			];
			for (var i in hashes) {
			    var hash = hashes[i];
				this[hash] = {};
				for (var j in Markers.DATA_TYPES) {
				    var type = Markers.DATA_TYPES[j];
					this[hash][type] = {};
				}
			}
			
			this.markerWorldsByType = {};
			this.currentMarkerType = 'PerspectiveView';
		};
		MarkerGrid.prototype.constructor = MarkerGrid;
		
		
		/* PUBLIC
		 * Change the visible type of marker (triggers marker display update).
		 */
		MarkerGrid.prototype.setMarkerType = function (newMarkerType, deferUpdate) {
			if (newMarkerType !== this.currentMarkerType) {
				this.currentMarkerType = newMarkerType;
				if (!deferUpdate) { // if deferUpdate is true, wait for next time update gets triggered by event
				    this.updateMarkerDisplay(newMarkerType);
				}
			}
		};
		
		
		/* PUBLIC
		 * Display the markers that should be in view, remove from map those that shouldn't. Displays the markers 
		 * currently aquired and requests those which should be in view (succesful requests trigger another 
		 * updateMarkerDisplay call).
		 */
		MarkerGrid.prototype.updateMarkerDisplay = function (markerType) {
			var map = SWMap.map;
			var zoom = map.getZoom();
			var view = map.getBounds();
			markerType = markerType || this.currentMarkerType;
			
			this.removeAllMarkersExceptOfType(markerType);
			
			if (markerType !== 'none' && zoom > 0) {
			    // get the markers that should be in view and put them in an object hashed by their hashId's
			    var inViewMarkers;
			    if (zoom >= Markers.Sector.MIN_ZOOM) {
				    inViewMarkers = this.getInViewMarkersFromSectors(view, zoom, markerType);
			    }
			    else {
				    inViewMarkers = this.getInViewMarkersFromMarkerWorld(view, zoom, markerType);
			    }
    			
			    this.removeAllMarkersOfTypeExceptTheseMarkers(markerType, inViewMarkers);
			    this.displayMarkers(markerType, inViewMarkers);
			    
			    if (general.numberOfMembers(inViewMarkers) === 0 && Markers.requestsPending === 0) {
			        SWMap.noMarkersAlertControl.show(Markers.DataType[markerType].plural);
			    }
			    else {
			        SWMap.noMarkersAlertControl.hide();
			    }
			}
			else {
			    SWMap.noMarkersAlertControl.hide();
			}
		};
		
		
		/* Return the markers of 'sectors' that are within 'view' (have them in an object hashed by their hashId's)
		 */
		MarkerGrid.prototype.getInViewMarkersFromSectors = function (view, zoom, markerType) {
		    var sectors = this.getInViewSectors(view, markerType);  // will request any sectors in view not already obtained
			var newMarkers = [];
			for (var i in sectors) {
				newMarkers = newMarkers.concat(sectors[i].getMarkersByZoom(zoom));
			}
			return this.getInViewMarkers(view, newMarkers);
		};
		
		
		/* Return the markers of markerWorld that are within 'view' (have them in an object hashed by their hashId's)
		 */
		MarkerGrid.prototype.getInViewMarkersFromMarkerWorld = function (view, zoom, markerType) {
		    var markerWorld = this.getMarkerWorld(markerType); // will request world if not already available
		    if (markerWorld) {
			    var markerSet = markerWorld.getMarkersByZoom(zoom);
			    return this.getInViewMarkers(view, markerSet);
			}
			else {
			    return {};
			}
		};
		
		
		/* Return object of markers within bounds 'view', hashed by their hashId's
		 */
		MarkerGrid.prototype.getInViewMarkers = function (view, markerSet) {
			var inViewMarkers = {};
			for (var i in markerSet) {
				var marker = markerSet[i];
				if (view.containsLatLng(marker.getLatLng())) {
					inViewMarkers[marker.hashId] = marker;
				}
			}
			return inViewMarkers;
		};
		

		/*
		 */
		MarkerGrid.prototype.displayMarkers = function (markerType, markersInView) {
			
			var displayedMarkers = this.displayedMarkersByType[markerType];
			var map = SWMap.map;
			
			for (var i in markersInView) {
				var marker = markersInView[i];
				var hashId = marker.hashId;
				
				if (!displayedMarkers[hashId]) {  // check first if it's already displayed to avoid unnecessary addOverlay() calls (not sure if doing so harms anything, but just in case)
					displayedMarkers[hashId] = marker;
					map.addOverlay(marker);
				}
			}
		};
		
		
		/* 'markersToKeep' is hash of markers by hashId
		 */
		MarkerGrid.prototype.removeAllMarkersOfTypeExceptTheseMarkers = function (markerType, markersToKeep) {
			var displayedMarkers = this.displayedMarkersByType[markerType];
			var displayedMarkersReplacement = {};	  // because there's no way of removing members from an object, we build a replacement hash so future iterations over displayedMarkers will only include displayed markers (*sigh*)							
			var map = SWMap.map;
			
			for (var i in displayedMarkers) {
				var marker = displayedMarkers[i];
				var hashId = marker.hashId;
				
				if (markersToKeep[hashId]) {
					displayedMarkersReplacement[hashId] = marker;
				}
				else {
					map.removeOverlay(marker);
				}		
			}
			
			this.displayedMarkersByType[markerType] = displayedMarkersReplacement;
		};
		
		
		/*
		 */
		MarkerGrid.prototype.removeAllMarkersOfType = function (markerType) {
			var displayedMarkers = this.displayedMarkersByType[markerType];
			var map = SWMap.map;
			for (var i in displayedMarkers) {
				map.removeOverlay(displayedMarkers[i]);
			}
			this.displayedMarkersByType[markerType] = {};
		};
		
		
		/* Remove every marker from the map that is not of type 'markerType'.
		 */
		MarkerGrid.prototype.removeAllMarkersExceptOfType = function (markerType) {
			for (var i in Markers.DATA_TYPES) {
			    var type = Markers.DATA_TYPES[i];
				if (type !== markerType) {
					this.removeAllMarkersOfType(type);
				}
			}
		};

		
		/* Return the sectors of type 'markerType' that are within or overlapping 'bounds' and which 
		 * have already been aquired. Any unaquired sectors in or overlapping bounds 
		 * will be requested. (This request will trigger updateMarkerDisplay() upon success after setting up the new sectors.)
		 */
		MarkerGrid.prototype.getInViewSectors = function (bounds, markerType) {
			
			var aquiredSectorsOfThisType = this.aquiredSectorsByType[markerType];
			
			var aquired = {};
			var unaquired = {};
		
			var ids = Markers.Sector.boundsToSectorIds(bounds);
			for (var i in ids) {
				var id = ids[i]
				var sector = aquiredSectorsOfThisType[id];
				if (sector) {
					aquired[id] = sector;
				} else {
					unaquired[id] = new Markers.Sector(id, markerType);
				}
			}
			
			if (general.numberOfMembers(unaquired) > 0) {  
				Markers.Sector.requestSectors(unaquired, markerType, this);
			}			
			
			return aquired;
		};
		
		
		/* Get the MarkerWorld for type 'markerType'. If not already aquired for type, then issue
		 * request for the markerWorld for that type and return null. (The request will 
		 * trigger updateMarkerDisplay() when successful.)
		 */
		MarkerGrid.prototype.getMarkerWorld = function (markerType) {
			var markerWorld = this.markerWorldsByType[markerType];

			if (markerWorld) {
				return markerWorld;
			} 
			else {
				Markers.MarkerWorld.requestMarkerWorld(markerType, this);
				return null;
			}
		};
		
		
		/*
		 */
		MarkerGrid.prototype.toString = function () {
			return 'MarkerGrid';
		};

})();
