﻿general.namespace('SW.Markers');

(function () {
        
        var Markers = SW.Markers;
        
        /* CONSTRUCTOR latIdx and lngIdx are coordinates in the world grid of sectors */
		var Sector = Markers.Sector = function (id, markerType) {
			this.id = id;
			this.bounds = Sector.sectorIdToBounds(id);
			this.markerType = markerType;
		};
		Sector.prototype.constructor = Sector;
	
	
		Sector.MIN_ZOOM = 6;
		Sector.MAX_ZOOM = 15;
		
		
		/* return bounds of the sector given its indices in the grid */
		Sector.calcBounds = function (latIdx, lngIdx) {
			var DEGREES_PER_SECTOR = Markers.DEGREES_PER_SECTOR;
			
			var W = (lngIdx - 1) * DEGREES_PER_SECTOR - 180;
			var E = W + DEGREES_PER_SECTOR;
			
			var S = (latIdx - 1) * DEGREES_PER_SECTOR - 90;
			var N = S + DEGREES_PER_SECTOR;
							
			return mapGeneral.newBounds(S, W, N, E);
		};
		
		
		/* return bounds of a sector given a sectorId (a string, e.g. "35-18") */
		Sector.sectorIdToBounds = function (sectorId) {
			var ids = sectorId.split("-");
			var latIdx = parseInt(ids[0]);
			var lngIdx = parseInt(ids[1]);
			
			return Sector.calcBounds(latIdx, lngIdx);
		};
		
		
		/* return sector id given a coord */
		Sector.coordToSectorId = function (coord) {

			// ensure coords are bounded within -90 to 90 lng and -180 to 180 lat
			var point = new GLatLng(coord.lat(), coord.lng(), false);
			var lat = point.lat();
			var lng = point.lng();
			
			var DPS = Markers.DEGREES_PER_SECTOR;
			
			// sectorLat should be [1, 180]
			var sectorLat = (lng === -90) ? 1 : Math.ceil((lat + 90)/ DPS);
			
			// sectorLng should be [1, 90]
			var sectorLng = (lng === -180) ? 1 : Math.ceil((lng + 180)/ DPS);
			
			return {lat: sectorLat, lng: sectorLng};
		};	
		
		
		/* gets list of ids that are in or overlap bounds */
		Sector.boundsToSectorIds = function (bounds) {
			var SW = bounds.getSouthWest();
			var NE = bounds.getNorthEast();            
			
			// ensure coords are bounded within -90 to 90 lng and -180 to 180 lat
			SW = new GLatLng(SW.lat(), SW.lng(), false);
			NE = new GLatLng(NE.lat(), NE.lng(), false);
						
			// sector ids, not coords
			var SWSectorId = Sector.coordToSectorId(SW);
			var NESectorId = Sector.coordToSectorId(NE);
			var S = SWSectorId.lat;
			var W = SWSectorId.lng;
			var N = NESectorId.lat;
			var E = NESectorId.lng;
			
			var sectorIds = [];
			var lat, lng;
			
			// yes, 'tis confusing
			var needsToCrossIntlDateLine = (W > E);
			for (lng = W; true; lng++) {
				if (needsToCrossIntlDateLine && lng > Markers.NUM_HORIZONTAL_SECTORS) {
					lng = 1;
					needsToCrossIntlDateLine = false;
				} 
				else if (!needsToCrossIntlDateLine && lng > E) {
					break;
				}
			
				// add every lat for lng
				for (lat = S; lat <= N; lat++) {
					sectorIds.push(lat + '-' + lng);
				}
			}			
			
			return sectorIds;
		};
		
		
		/*
		 */   
		Sector.requestSectors = function (sectorsToAquire, markerType, markerGrid) {

			var aquiredSectors = markerGrid.aquiredSectorsByType[markerType];
			var pendingSectorIds = markerGrid.sectorIdsPendingRequestByType[markerType];
			
			// get list of ids for sectors to aquire as a comma-separated string
			var idsOfSectorsToAquire = [];
			for (var id in sectorsToAquire) {
				if (!aquiredSectors[id] && !pendingSectorIds[id]) {
					idsOfSectorsToAquire.push(id);
					pendingSectorIds[id] = true;
				}
			}
			var sectorIdsString = idsOfSectorsToAquire.join(',');
			
			if (idsOfSectorsToAquire.length > 0) {
			    YAHOO.util.Connect.asyncRequest(
                    'POST',
                    '/WebServices/DataPoints.asmx/GetByTypeAndSectorIdList?r=' + general.rand(),
                    {
                        success: Sector.onSectorRequestSuccess,
                        failure: Sector.onSectorRequestFailure,
                        argument: {
                            aquiredSectors: aquiredSectors,
						    pendingSectorIds: pendingSectorIds,
						    sectorsToAquire: sectorsToAquire,
						    markerGrid: markerGrid
                        }
                    },
                    JSON.encode({  // post data
                        dataType: markerType,
                        sectorIdList: sectorIdsString
                    
                    })
                );
                
                Markers.requestsPending++;
                
                SWMap.loadingIndicator.addLoad();
			}
		};
		
		
		Sector.numSectorRequestFailuresSinceLastSuccess = 0;
		
		/*
		 */
		Sector.onSectorRequestSuccess = function (response) {
		    SWMap.loadingIndicator.removeLoad();
		
		    Sector.numSectorRequestFailuresSinceLastSuccesss = 0;
		
			var id, sector;
			var points = general.evalXmlResult(response.responseXML);

            if (points) {
										
			    // add the newly aquired points, hasing them by sector
			    var pointsBySectorId = {};
			    for (var i = 0; i < points.length; i++) {
				    var p = points[i];
    				
				    var point = {
					    id: p.I,
					    sectorId: p.SI,
					    lat: p.Lat,
					    lng: p.Lng,
					    name: p.N,
					    dataTypeId: p.T,
					    count: p.C
				    };
    				
				    id = point.sectorId;
				    if (!pointsBySectorId[id]) {
					    pointsBySectorId[id] = [];
				    }
				    pointsBySectorId[id].push(point);
			    }
    			
			    var args = response.argument;
    			
			    // create markers for newly aquired sectors and add them to aquiredSectors
			    for (id in args.sectorsToAquire) {
				    sector = args.sectorsToAquire[id];
				    points = pointsBySectorId[id] || [];
				    sector.setPoints(points);
				    args.aquiredSectors[id] = sector;
				    args.pendingSectorIds[id] = null;  // aquired sectors no longer pending
			    }

                Markers.requestsPending--;
    						
			    args.markerGrid.updateMarkerDisplay(); // now that we have the markers, we can display them
			}
			else {
			    Sector.onSectorRequestFailure(response);
			}
		};
		
		
		/*
		 */
		Sector.onSectorRequestFailure = function (response) {
		    SWMap.loadingIndicator.removeLoad();
		
		    general.reportClientError('Marker sector request failure.');
			
			Sector.numSectorRequestFailuresSinceLastSuccess++;
			
			var args = response.argument;
			
			// don't prevent future requests for the sectors we failed to get
			for (var id in args.sectorsToAqure) {
				args.pendingSectorIds[id] = null;
			}
			
			Markers.requestsPending--;
			
			if (Sector.numSectorRequestFailuresSinceLastSuccess <= 2) {  // cap the number of retries            
			    args.markerGrid.updateMarkerDisplay();
		    } else {
		        general.reportClientError('Sector request failure: ' + response.responseText);
		    }
		};
		
		
		/* get the markers of the sector for a particular type and zoom level */
		Sector.prototype.getMarkersByZoom = function (zoom) {
			var points = this.points;
			var markerType = this.markerType;
			var bounds = this.bounds;
			var newMarkers;		    
			
			if (zoom > Sector.MAX_ZOOM) {
				if (!this.unclusteredMarkers) {
					this.unclusteredMarkers = [];
					for (var i = 0; i < points.length; i++) {
						var marker = Markers.createSingleMarker(points[i], markerType);
						this.unclusteredMarkers.push(marker);
					}        
				}
				newMarkers = this.unclusteredMarkers;    
			} 
			else if (zoom >= Sector.MIN_ZOOM) {
				if (!this.markersByZoom[zoom]) {
					
					this.markersByZoom[zoom] = Markers.createMarkers(
						points,
						bounds,
						markerType,
						Math.pow(2, (zoom - 5)) // number of sections (horizontally and vertically). This should double for every zoom level
					);
				}
				newMarkers = this.markersByZoom[zoom];
			} 
			else {
				throw "invalid zoom level for getMarker request to sector";
			}
			
			return newMarkers;
		};
		
		
		/* creates markers for the current sector such that markers too close to each other are grouped into one special marker */
		Sector.prototype.setPoints = function (points) {
			this.points = points;
			this.markersByZoom = {};
		};

})();
