// global variables used by the map
var map;

var subwayMarkers = new Array();
var subwayMarkersApplied = false;
var subwayMarkersTileLayer;

var schoolMarkers = new Array();
var schoolMarkersApplied = false;
var schoolMarkersTileLayer;

var libraryMarkers = new Array();
var libraryMarkersApplied = false;
var libraryMarkersTileLayer;

var baseIcon = new GIcon();
var blankIcon = new GIcon();
var now = new Date();


/**
 * Google map instance wrapper (used throughout any frontend map appearance)
 */
function MyMap()
{
    this.closestNeighborhood = false;
    this.closestNeighborhoodWithResults = false;
}

MyMap.prototype = new Object();

/**
 * Loads the map -- initialize GMap, EWindow, cookie jar (for storing recent selections), setting up map parameters and 
 * registering event callbacks
 */
MyMap.prototype.loadMap = function(type) {
    if (!GBrowserIsCompatible()) {
        alert("Sorry, your browser is incompatible with this page.");
        return;
    }

    this.containingDiv = document.getElementById("map");
    this.map = new GMap2(this.containingDiv);
  	this.eW = new EWindow(this.map, E_STYLE_1);
    this.mapBehaviour = 'default';
    this.resultsNamespace = 'borough';
    this.limitResultsToVenueType = false;
    this.limitResultsToVenueId = false;
    this.limitResultsToCollectionId = false;

    this.recentSelectionsListId = 'recent_selections_list';
    this.recentSelectionsLimit = 5;
    this.cookieJar = new CookieJar({'expires' : '; expires=0', 'path' : '/'});
    
    // check stored recent selections
    if (this.cookieJar.get('map_recent_selections')) {
      this.recentSelections = this.cookieJar.get('map_recent_selections');
      this.refreshRecentSelectionsContainer(); // build links
    } else {
      this.recentSelections = Array();      
    }
    
    //the following variables control the ability to zoom to different levels
    this.zoomingAlready = false;
    this.currentRelativeZoomLevel = 0;
  	this.allowedZoomLevels = Array(10,12,13,15,17);

    // Create the tile layer overlay and 
    // implement the three abstract methods                 
    var tilelayer = new GTileLayer(null,10,17,{opacity:1.0, isPng:true, tileUrlTemplate: 'http://67.192.109.227:8080/tiles/base/{Z}/{Y}/{X}.png'});

    var myTileLayer = new GTileLayerOverlay(tilelayer);
    
    // the default center lat/long values are more accurately defined in defaultCenterAndZoom to force a slight map move (need for IE6)
    this.map.setCenter(new GLatLng(40.74, -74.00), this.allowedZoomLevels[this.currentRelativeZoomLevel]);
    this.map.addOverlay(myTileLayer);

    // disable double click zoom
    this.map.disableDoubleClickZoom();

  	// create marker manager 
  	this.mm = new MarkerManager(this.map); 
	
  	// add EWindow Overlay
  	this.map.addOverlay(this.eW);
	
    // base icon attributes from which all other icons will start
    baseIcon.shadow = "";
    baseIcon.iconSize = new GSize(11, 11);
    baseIcon.shadowSize = new GSize(0, 0);
    baseIcon.iconAnchor = new GPoint(5,5);
    baseIcon.infoWindowAnchor = new GPoint(9, 2);

    // set up a blank icon for click and zoom
    blankIcon.iconSize = new GSize(12, 12);
    blankIcon.shadowSize = new GSize(1, 1);
    blankIcon.iconAnchor = new GPoint(1, 1);
    blankIcon.infoWindowAnchor = new GPoint(1, 1);
    blankIcon.infoShadowAnchor = new GPoint(1, 1);
    blankIcon.image = "";
    blankIcon.shadow = "";

    this.map.enableContinuousZoom();

    this.crosshairDiv = this.createCrosshair();
    this.busyDiv = this.createBusy();

    this.results = new Results(this.map, type, this.mm);
    this.resultsPaginator = new ResultsPaginator(this.results, this);

    this.boroughs = new Boroughs(this);
    this.boroughs.loadAll();

    this.nearbyMarker = new NearbyMarker(this.map);

    if (type == 'full') {
  		this.map.addControl(new NavigationControll());
    } else if (type == 'collection') {
      // no map control on collection map
    }
    
    var self = this;

    // Register event listeners
    GEvent.addListener(this.map, "zoomend", function(zoomOld, zoomNew) {
      var zoomTo = false;
      if (zoomNew < self.allowedZoomLevels[0]) {
        // lower than the lower bound
        zoomTo = self.allowedZoomLevels[0];
      } else if (zoomNew > self.allowedZoomLevels[4]) {
        // higher than the upper limit
        zoomTo = self.allowedZoomLevels[4];
      } else if (!self.allowedZoomLevels.include(zoomNew)) {
        // within acceptable range, but not exact match
        if (zoomNew > zoomOld) {
          // zoom in performed
          zoomTo = zoomNew + 1; // should be a hit (11 -> 12; 14 -> 15)
        } else {
          zoomTo = zoomNew - 1;
        }
      }
      if (zoomTo) {
        //console.log('Forced zoom to ' + zoomTo + ' - requested value was ' + zoomNew);
        self.map.setZoom(zoomTo);
      }
      // used for reclustering markers 
      self.results.refresh(true);
    });
    GEvent.addListener(this.map, "movestart", function() {
        self.results.saveAndCloseResultInfo();
        self.crosshairDiv.style.visibility = 'visible';
    });
    GEvent.addListener(this.map, "moveend", function() {
        // Move may have not fired an event for the very last position, make sure we are up to date
        self.crosshairDiv.style.visibility = 'hidden';
        self.results.restoreLastOpenInfo();
    });
}

/**
 * Clears all result markers
 */
MyMap.prototype.clearResults = function() {
  if (this.results) {
    this.results.removeMarkers();
    this.results.closeResultInfo();
    this.eW.hide();
    this.results.clearResults();    
  }
}

/**
 * Reloads the map results
 */
MyMap.prototype.reloadResults = function() {
  this.updateClosestNeighborhood(true);
}

/**
 * Recent selections toolkit
 */
MyMap.prototype.addRecentSelection = function(result) {
  this.recentSelections = this.recentSelections.slice(-1 * this.recentSelectionsLimit); // slice it
  if (this.recentSelections.indexOf(result[1]) == -1) {
    // ignore duplicates
    this.recentSelections.push(result[1]);
    this.cookieJar.put('map_recent_selections', this.recentSelections);
    this.refreshRecentSelectionsContainer();
  }
}
MyMap.prototype.refreshRecentSelectionsContainer = function() {
  if (this.mapBehaviour != 'collections' && this.recentSelections.length && $(this.recentSelectionsListId)) {
    var noResultsItems = $$('#' + this.recentSelectionsListId + ' .afta_txt_gray');
    if (noResultsItems.length) {
      noResultsItems[0].hide();
    }
    var items = '';
    var elements = this.recentSelections.reverse(false); // false - clone object
    // limit to 5 elements (output them reversed)
    var limit = elements.length > this.recentSelectionsLimit - 1 ? this.recentSelectionsLimit : elements.length;
    for (var i = 0; i < limit; i++) {
      result = elements[i];
      items += '<li><a href="/map/index?lat=' + result['lat'] + '&long=' + result['long'] + '&venue_type=' + result['type'] + '&venue_id=' + result['id'] + '">' + result['name'] + '</a></li>';
    }
    $(this.recentSelectionsListId).innerHTML = items;
  }
}

/**
 * Updates all markers with the request response from the backend results call
 */
MyMap.prototype.updateMarkers = function(request) {
	var response = request.responseText;
	var page = 0;
	if (response.length){
  	var xmlDoc;	  
  	if (window.ActiveXObject){
  		xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
  		xmlDoc.async = 'false';
  		xmlDoc.loadXML(response);
  	} else {
  		var parser = new DOMParser();
  		xmlDoc = parser.parseFromString(response, "text/xml");
  	}
  	this.results.clearResults();
    this.resultsPaginator.clearLinks();
    //grab total number of possible results
    var total = xmlDoc.documentElement.getElementsByTagName('total')[0].childNodes[0].nodeValue;
    //tell the pager how many results are in the database
    this.resultsPaginator.setTotal(total);
    //grab total number of possible results
    page = xmlDoc.documentElement.getElementsByTagName('page')[0].childNodes[0].nodeValue;
  	var orgs = xmlDoc.documentElement.getElementsByTagName('org');
  	//console.dir(orgs);
  	for (var i = 0; i < orgs.length; i++){
  		var lat = orgs[i].getElementsByTagName('lat')[0].childNodes[0].nodeValue;
  		var long = orgs[i].getElementsByTagName('long')[0].childNodes[0].nodeValue;
  		var id = orgs[i].getElementsByTagName('id')[0].childNodes[0].nodeValue;
  		var venue_type = orgs[i].getElementsByTagName('venue_type')[0].childNodes[0].nodeValue;
  		var orgName = orgs[i].getElementsByTagName('name')[0].childNodes[0].nodeValue;
  		if (orgs[i].getElementsByTagName('desc')[0].childNodes.length){
    		var desc = orgs[i].getElementsByTagName('desc')[0].childNodes[0].nodeValue;		  
  		} else {
  		  var desc = '';
  		}
  		if (orgs[i].getElementsByTagName('address')[0].childNodes.length){
    		var address = orgs[i].getElementsByTagName('address')[0].childNodes[0].nodeValue;		  
  		} else {
  		  var address = '';
  		}
  		if (orgs[i].getElementsByTagName('phone')[0].childNodes.length){
    		var phone = orgs[i].getElementsByTagName('phone')[0].childNodes[0].nodeValue;		  
  		} else {
  		  var phone = '';
  		}
  		var url = orgs[i].getElementsByTagName('url')[0].childNodes[0].nodeValue;
  		this.results.pushResult([new GLatLng(lat, long), {
  			'name': orgName,
  			'desc': desc,
  			'address': address,
  			'phone': phone,
  			'url': url,
  			'image': 'n/a',
  			'type' : venue_type,
  			'id' : id,
  			'lat' : lat,
  			'long' : long
  		}]);

      if (this.mapBehaviour == 'default'){
        this.resultsPaginator.pushLink("<li><a href='javascript:void(0)' onclick='view_organization(" + lat + ", " + long + ", " + i +"); return false;'>" + orgs[i].getElementsByTagName('name')[0].childNodes[0].nodeValue + "</li></span>");      
      } else if (this.mapBehaviour == 'search') {
        this.resultsPaginator.setResultsContainerId('afta_map_results');
        this.resultsPaginator.pushLink('<li><p class="afta_map_listnum">' + (i+1) + '</p><div><p class="afta_subhead"><a href="'+ url +'">' + orgName + '</a></p>' + desc + '</div></li>');
      } else if (this.mapBehaviour == 'collections') {
        var venueType = 'org';
        if (venue_type == 'venues'){
          venueType = 'venue';
        }
    		if (orgs[i].getElementsByTagName('collection_item_id')[0].childNodes.length){
      		var collId = orgs[i].getElementsByTagName('collection_item_id')[0].childNodes[0].nodeValue;		  
    		} else {
    		  var collId = '';
    		}
        if ($('collection_item_' + collId)) {
          var itemBullet = $('collection_item_' + collId);
          var itemIndex = i + 1;
          itemBullet.update("<a href='#afta_content_head' onclick='view_organization(" + lat + ", " + long + ", " + i +");'>" + itemIndex + "</a>");
          itemBullet.setStyle({visibility : 'visible'});          
        }
      }

  	}
	  
	} // end if any response (ie. not empty set)
	
	//console.log(this.results.results);
	if (this.mapBehaviour == 'default' || this.mapBehaviour == 'search'){
    this.resultsPaginator.refresh();
    this.resultsPaginator.setPage(page);
  } else if (this.mapBehaviour == 'collections') {
    // collections don't have a results paginator, so all items have to be pushed as active results!
    this.results.clearActiveResults();
    for (var i = 0; i < this.results.results.length; i++) {
      this.results.pushActiveResultIndex(i);
    }
    this.results.refresh(true);
  }
}

/**
 * Updates the map with the current closest neighborhood
 * Depending on the map state, doesn't do anything except updating the current borough caption, or tries to reload a new
 * results data set (for eg. when browsing the neghborhoods list)
 */
MyMap.prototype.updateClosestNeighborhood = function(updateResults)
{
    var newClosestNeighborhood = this.boroughs.findClosestNeighborhood();

    // hook mini-map override
    if (this.mapBehaviour == 'collections' && this.results.results.length) {
      this.results.restoreLastOpenInfo();
      return true;
    }

    if (newClosestNeighborhood != this.closestNeighborhood) {
        if (this.closestNeighborhood != false) {
            this.closestNeighborhood.removeOverlay();
            this.results.clearResults();
            this.closestNeighborhoodWithResults = false;
            this.results.refresh(false);
        }

        this.closestNeighborhood = newClosestNeighborhood;
        // Sidebar
        this.closestNeighborhood.boroughName = this.closestNeighborhood.boroughName == 'Manhatten' ? 'Manhattan' : this.closestNeighborhood.boroughName;
        
        if (this.mapBehaviour == 'default' && $("afta_sidebar_borough_name")){
          //document.getElementById("afta_sidebar_neighborhood_name").innerHTML = this.closestNeighborhood.neigborhoodName;
          if (this.resultsNamespace != 'all') {
            $("afta_sidebar_borough_name").innerHTML = this.closestNeighborhood.boroughName;            
          } else {
            $("afta_sidebar_borough_name").innerHTML = 'All Boroughs';
          }
        }

        /* -- don't show neigh. overlay
        if (this.closestNeighborhood != false && this.resultsNamespace == 'neighborhood') {
            this.closestNeighborhood.addOverlay();
        }
        */
        
        if (this.mapBehaviour == 'default' && $("neighborhoods_select")){
          // update the left side neighborhoods select box
          var sel_box_options = $("neighborhoods_select").options;
          for (var i = 0; i < sel_box_options.length; i++){
            if (sel_box_options[i].text.strip() == this.closestNeighborhood.neigborhoodName.strip()){
              sel_box_options[i].selected = 'selected';
              break;
            }
          }
        }
        
    }

    if (updateResults && this.closestNeighborhood != false) {
        if (true /*newClosestNeighborhood != this.closestNeighborhoodWithResults*/) {
            // Results are out of date, update markers result set
            var hood = this.closestNeighborhood;
      			this.updateResults(0,false);
            this.closestNeighborhoodWithResults = hood;
        }
        else
        {
            // Results are up to date, just restore thhe last open info
            this.results.restoreLastOpenInfo();
        }
    }
}

/**
 * Updates the results/markers for the current page
 * Performs a prototype Ajax call for fetching the new results set
 */
MyMap.prototype.updateResults = function(page, isNewPage)
{
	var hood = this.closestNeighborhood;
	
	// request new results from server
	this.map.disableDragging();
	this.busyDiv.style.visibility = 'visible';
	var self = this;
	baseDataUrl = '/fo.php/map/getMapData/';
	//determine if there was any search terms (JESSE)
	if (searchterms.length > 0) {
	  baseDataUrl = baseDataUrl + 'q/' + searchterms + '/' + searchparams;
	}
	if (self.limitResultsToVenueType && self.limitResultsToVenueId) {
	  baseDataUrl = baseDataUrl + 'venue_type/' + self.limitResultsToVenueType + '/venue_id/' + self.limitResultsToVenueId + '/';
	}
	if (self.limitResultsToCollectionId) {
	  baseDataUrl = baseDataUrl + 'collection_id/' + self.limitResultsToCollectionId + '/';
	}
	if (page > 0) {
		baseDataUrl += 'page/' + page + '/';
	}
	var hoodBorough = '';
	if (hood.boroughName && hood.neigborhoodName) {
	  hoodBorough = 'borough/' + hood.boroughName + '/neighborhood/' + hood.neigborhoodName.strip() + '/';
	}
	new Ajax.Request(baseDataUrl + hoodBorough + 'results/' + this.resultsNamespace,
	    {
	        asynchronous:true,
	        evalScripts:true,
	        onSuccess:function(request, json){ self.updateMarkers(request); },
	        onComplete:function(request, json) {
	            self.busyDiv.style.visibility = 'hidden';
	            self.map.enableDragging();
	        }
	    }
	);
}

/**
 * Generates the map cross-hair icon marking its center
 */
MyMap.prototype.createCrosshair = function()
{
    var crosshairDiv = document.createElement('div');
    this.crosshairDiv = crosshairDiv;
    crosshairDiv.style.visibility = 'hidden';
    crosshairDiv.style.position = 'absolute';
    crosshairDiv.style.left = ((this.containingDiv.offsetWidth - 27) / 2) + 'px';
    crosshairDiv.style.top = ((this.containingDiv.offsetHeight - 27) / 2) + 'px';
    crosshairDiv.style.zIndex = '1'; // layered
    crosshairDiv.style.border = '';
    var crosshairImg = document.createElement('img');
    crosshairImg.setAttribute('src', '/images/icons/crosshair.png');
    crosshairImg.style.position = 'absolute';
    crosshairImg.style.left = '0px';
    crosshairImg.style.top = '0px';

    crosshairDiv.appendChild(crosshairImg);
    this.containingDiv.appendChild(crosshairDiv);

    return crosshairDiv;
}

/**
 * Generates the map's "busy" overlay surfaced when performing backend calls
 */
MyMap.prototype.createBusy = function()
{
    var busyDiv = document.createElement('div');
    this.busyDiv = busyDiv;
    busyDiv.style.visibility = 'hidden';
    busyDiv.style.position = 'absolute';
    busyDiv.style.left = ((this.containingDiv.offsetWidth - 27) / 2) + 'px';
    busyDiv.style.top = ((this.containingDiv.offsetHeight - 27) / 2) + 'px';
    busyDiv.style.zIndex = '1'; // layered
    busyDiv.style.border = '';
    var busyImg = document.createElement('img');
    busyImg.setAttribute('src', '/images/icons/ajax-indicator2.gif');
    busyImg.style.position = 'absolute';
    busyImg.style.left = '0px';
    busyImg.style.top = '0px';

    busyDiv.appendChild(busyImg);
    this.containingDiv.appendChild(busyDiv);

    return busyDiv;
}

/**
 *Add the subway markers to the map
 */
MyMap.prototype.addSubwayMarkers = function () {
	
    if (!subwayMarkersApplied) {
        //var len = subwayMarkers.length;
        /*for (i = 0; i < len; ++i) {
            subwayMarkers[i].show();
        }*/
        
        var len = sbwy.length;
      for (var i = 0; i < len; i++) {
          point = sbwy[i];
          var point = new GLatLng(point.lat,point.lon);
          //subwayMarkers[i]=createMarker(point, sbwy[i]);
          subwayMarkers[i]=createMarker(point, sbwy[i], true);
          this.mm.addMarker(subwayMarkers[i],0,17); 
          //this.map.addOverlay(subwayMarkers[i]);
          //subwayMarkers[i].hide();
      }
        
        
        
	    var myCopyright = new GCopyrightCollection("� ");
	      myCopyright.addCopyright(new GCopyright('Funny Garbage',
	          new GLatLngBounds(new GLatLng(-90,-180), new GLatLng(90,180)),
	          0,''));
		
	    // Create the tile layer overlay and 
	    // implement the three abstract methods                 
	    var tilelayer = new GTileLayer(myCopyright);
	    tilelayer.getTileUrl = function(thePoint,zoom) {
	        url = 'http://67.192.109.227:8080/tiles/subways/' + zoom + '/' + thePoint.y + '/' + thePoint.x + '.png';
	        return url;
	    };
	    tilelayer.isPng = function() { return true;};
	    tilelayer.getOpacity = function() { return 1.0; }
	    
	    subwayMarkersTileLayer = new GTileLayerOverlay(tilelayer);
	 	myMap.map.addOverlay(subwayMarkersTileLayer);
	 	
	 	subwayMarkersApplied=true;
        return;
    }
}

/**
 * Clear the subway markers from the map
 */
MyMap.prototype.clearSubwayMarkers = function () {
    if (subwayMarkersApplied) {
        var len = subwayMarkers.length;
        for (i = 0; i < len; ++i) {
            //subwayMarkers[i].hide();
            this.mm.removeMarker(subwayMarkers[i]);
        }
        myMap.map.removeOverlay(subwayMarkersTileLayer);
        subwayMarkersApplied=false;
    }
}

/**
  * Add the school markers to the map
  */
MyMap.prototype.addSchoolMarkers = function () {
    if (!schoolMarkersApplied) {
        //var len = schoolMarkers.length;
        /*for (i = 0; i < len; ++i) {
            schoolMarkers[i].show();
        }*/
      
            // create an array of markers for the schools
      var len = smarkers.length;
      for (index = 0; index < len; ++index) {
          marker = createGxMarker(smarkers[index],true);
          schoolMarkers[index]=marker;
          this.mm.addMarker(schoolMarkers[index],15,17);
          //this.map.addOverlay(schoolMarkers[index]);
          //schoolMarkers[index].hide();
      }
        
        
	    var myCopyright = new GCopyrightCollection("� ");
	      myCopyright.addCopyright(new GCopyright('Funny Garbage',
	          new GLatLngBounds(new GLatLng(-90,-180), new GLatLng(90,180)),
	          0,''));
	    // Create the tile layer overlay and 
	    // implement the three abstract methods                 
	    var tilelayer = new GTileLayer(myCopyright);
	    tilelayer.getTileUrl = function(thePoint,zoom) {
	        url = 'http://67.192.109.227:8080/tiles/schools/' + zoom + '/' + thePoint.y + '/' + thePoint.x + '.png';
	        return url;
	    };
	    tilelayer.isPng = function() { return true;};
	    tilelayer.getOpacity = function() { return 1.0; }
	    schoolMarkersTileLayer = new GTileLayerOverlay(tilelayer);
	 	myMap.map.addOverlay(schoolMarkersTileLayer);
	 	
        schoolMarkersApplied=true;
        
      // Florin: requested removal of enforced zoom level
	 	  /*var currentZoomLevel = this.allowedZoomLevels[3];
		  this.map.setZoom(currentZoomLevel);*/
      return;        
    }
}

/**
 * Clear the school markers from the map
 */
MyMap.prototype.clearSchoolMarkers = function () {
    if (schoolMarkersApplied) {
        var len = schoolMarkers.length;
        for (i = 0; i < len; ++i) {
            //schoolMarkers[i].hide();
            this.mm.removeMarker(schoolMarkers[i]);
        }
        myMap.map.removeOverlay(schoolMarkersTileLayer);
        schoolMarkersApplied=false;
    }
}

/**
 *Add the library markers to the map
 */
MyMap.prototype.addLibraryMarkers = function () {
    if (!libraryMarkersApplied) {
        //var len = libraryMarkers.length;
        /*for (i = 0; i < libraryMarkers.length; ++i) {
            libraryMarkers[i].show();
        }*/
        
      // create an array of markers for the libraries
      var len = lmarkers.length;
      for (index = 0; index < len; ++index) {
          libraryMarkers[index]=createGxMarker(lmarkers[index],false);
          this.mm.addMarker(libraryMarkers[index],15,17);
          //this.map.addOverlay(libraryMarkers[index]);
          //libraryMarkers[index].hide();
      }
        
	    var myCopyright = new GCopyrightCollection("� ");
	      myCopyright.addCopyright(new GCopyright('Funny Garbage',
	          new GLatLngBounds(new GLatLng(-90,-180), new GLatLng(90,180)),
	          0,''));

	    // Create the tile layer overlay and 
	    // implement the three abstract methods                 
	    var tilelayer = new GTileLayer(myCopyright);
	    tilelayer.getTileUrl = function(thePoint,zoom) {
	        url = 'http://67.192.109.227:8080/tiles/libraries/' + zoom + '/' + thePoint.y + '/' + thePoint.x + '.png';
	        return url;
	    };
	    tilelayer.isPng = function() { return true;};
	    tilelayer.getOpacity = function() { return 1.0; }

	    libraryMarkersTileLayer = new GTileLayerOverlay(tilelayer);

	 	  myMap.map.addOverlay(libraryMarkersTileLayer);
	 	
	 	  libraryMarkersApplied=true;
	 	
	 	
	 	  // Florin: requested removal of enforced zoom level
  	 	/*var currentZoomLevel = this.allowedZoomLevels[3];
  		this.map.setZoom(currentZoomLevel);*/
      return;
        
    }
}

/**
 * Remove the library markers from the map
 */
MyMap.prototype.clearLibraryMarkers = function () {
    if (libraryMarkersApplied) {
        var len = libraryMarkers.length;
        for (i = 0; i < len; ++i) {
            //libraryMarkers[i].hide();
            this.mm.removeMarker(libraryMarkers[i]);
        }
        myMap.map.removeOverlay(libraryMarkersTileLayer);
        libraryMarkersApplied=false;
    }
}

myMap = new MyMap();

var groupingLimit = 10;

