/*************************************************************
 * Map class: Handles the map and interacts with other geometry classes (Point/Polygon)
 *************************************************************/
function Map() {
	this.map = null;
	this.submissionMode = false;
	this.geometryMode = "point";
	this.geometryObj = null;
	this.geometryArray = new Array();
	this.label = null;	
	this.index = 0;
    this.communityCluster = []; // an array to store all the community markers (only points)
    this.officialCluster = []; // an array to store all the official markers (only points)
    this.communityClusterer = null;
    this.officialClusterer = null;
}

Map.prototype.getMap = function() {
	return this.map;
};

Map.prototype.setSubmissionMode = function(val) {
	this.submissionMode = val;
};

Map.prototype.getSubmissionMode = function () {
	return this.submissionMode;
};

Map.prototype.setGeometryMode = function(val) {
	this.geometryMode = val;
};

Map.prototype.getGeometryMode = function() {
	return this.geometryMode;
};

Map.prototype.addControl = function(section) {
	switch(section) {
		case "home":
			GEvent.addListener(this.map,"click",function(overlay,point) {
				window.location=webroot+'oaks/index/'+overlay.id;
			});
			break;
		default:
			GEvent.bind(this.map, "click", this, this.leftClick);
			break;
	}
};

Map.prototype.buildMap = function (mapdiv, zoom, center) {
	zoom = zoom || 6;
	center = center || new GLatLng(37.4419, -122.1419);

	var map = new GMap2(mapdiv, {draggableCursor:"auto", draggingCursor:"move", mapTypes:[G_PHYSICAL_MAP,G_HYBRID_MAP]});
	
	// Load initial map and a bunch of controls
	map.setCenter(center, zoom);
	//map.addControl(new GSmallZoomControl());
	map.addControl(new GLargeMapControl())
	
	// Create a hierarchical map type control
	var hierarchy = new GHierarchicalMapTypeControl();
	hierarchy.addRelationship(G_PHYSICAL_MAP, G_HYBRID_MAP,'Hybrid', false);
	map.addControl(hierarchy);
	map.addControl(new GScaleControl()); // Scale bar
	map.disableDoubleClickZoom();
	
	this.map = map;
};

/*
* This function responds to a user's interaction with the map
*/ 
Map.prototype.leftClick = function (overlay, point) {
	// When the user click on the report tab, the submissionMode is activated.
	// Then the user can add points or polygon to the map.
	if (this.submissionMode) {
		if (point) {
			// May not need the modal structure
			if (this.geometryMode == "point") {
				if (this.geometryObj == null || this.geometryObj instanceof Polygon) {
					this.geometryObj = new Point(this.map);
				}
				this.geometryObj.add(point); // add and redraw a point
			} else if (this.geometryMode == "polygon") {
				if (this.geometryObj == null || this.geometryObj instanceof Point) {
					this.geometryObj = new Polygon(this.map);
				}
				this.geometryObj.add(point); // add a point and redraw the polygon
			} else { // when address mode is chosen
				// nothing happens when the map is clicked
			}
		} else {
			// Display a tool tip that says you are here
			if (overlay.id) {
				$('#oaksInfo > ul').tabs('select',0);
				this.clear();
				this.setSubmissionMode(false);
				// Find the index the overlay that is clicked, which will be used for scrolling
				for (var i=0; i<this.geometryArray.length; i++) {
					if (this.geometryArray[i].getID() == overlay.id) {
						this.index = i; 
						break;
					}
				}
			}
		}
	} else {
		if (overlay) {
			this.addLabel(overlay);
			/*
			for (var i=0; i<this.geometryArray.length; i++) {
				if (this.geometryArray[i].getID() == overlay.id) {
					this.index = i; 
					break;
				}
			}
			*/
		}
	}
};

Map.prototype.addLabel = function(overlay) {
	if (this.label == null) {
		var msg = '<div style="padding: 0px 0px 8px 8px; background: url('+webroot+'img/point_bottom_left.png) no-repeat bottom left;"><div style="background-color: #f2efe9; padding: 2px;width:80px">Look here...</div></div>';
		this.label = new ELabel(overlay.center, msg, null, null, 80);
		this.label.id = overlay.id;
		this.map.addOverlay(this.label);
	} else {
		this.label.id=overlay.id;
		this.label.setPoint(overlay.center);
		this.showLabel();
	}
};

Map.prototype.showLabel = function() {
	this.label.show();
}

Map.prototype.hideLabel = function() {
	this.label.hide();
};

/*
 * Clears a newly added geometry object on the map.
 */
Map.prototype.clear = function () {
	if (this.geometryObj != null) {
		this.geometryObj.clear();
	}
	$('input:hidden#CommunityOakGeom').val("");
};

Map.prototype.clearAll = function(){
	this.map.clearOverlays();
}

// Load points and polygons 
Map.prototype.load = function(addControl, url, centerLast, selected_oak_id) { // default control is false
	url = url || webroot+"resources/json/all";
	centerLast = centerLast || false;
    var geo = {};
    var thisObj=this;
    var initialGeo = null;
    
	$('#loadingDiv').show();

    $.getJSON(url, function(data, status) {
    	$.each(data.oaks, function(i, oak){
    		geo.id =oak.guid;
    		geo.type = oak.geomType;
    		geo.submitType = oak.submitType;
    		if (geo.type == "POINT") {
    		    var point = new GLatLng(parseFloat(oak.point[0]),parseFloat(oak.point[1]));
    		    var geoObj = new Point(thisObj.map);
    			geoObj.load(point, geo, addControl);
    		} else {
    			var pointArray = new Array();
    			for (var j=0; j<oak.polygon.length; j++) {
    			    pointArray.push(new GLatLng(parseFloat(oak.polygon[j][0]),parseFloat(oak.polygon[j][1])));
    			}
    			var geoObj = new Polygon(thisObj.map);
    			geoObj.load(pointArray, geo, addControl);
    		}
		
			// A geometry object (a point or a polygon) draw itself on the map 
			geoObj.draw();
			
			// Alternatively, we can do clustering and then draw
			/*
			if (geo.type=="POINT"){
				if (geo.submitType == "CommunityOak") {
					thisObj.communityCluster.push(geoObj.get());
				} else {
					thisObj.officialCluster.push(geoObj.get());
				}
				//progressBar.updateLoader(1);
			} else {
				//progressBar.updateLoader(1);
				geoObj.draw();
			}
			*/
			
			// Push a geometry object into an array for management
			thisObj.geometryArray.push(geoObj);
			
			if (selected_oak_id && selected_oak_id == geo.id) {
				initialGeo = geoObj;
				// set the index for scrolling through the points
				thisObj.index = thisObj.geometryArray.length-1;
			}
		});
		
		  // After loading all the points, we can draw clusters
    	/*
		  thisObj.communityClusterer = 
			  new MarkerClusterer(thisObj.map, thisObj.communityCluster, 
					  {maxZoom: 11, styles: clusterStyles(0)});
		  thisObj.officialClusterer = 
			  new MarkerClusterer(thisObj.map, thisObj.officialCluster, 
					  {maxZoom: 11, styles: clusterStyles(1)});
		 */
    	
		  if (initialGeo){
			initialGeo.center();
			//initialGeo.draw(); // Need to draw this point individually because of the clustering issue
			thisObj.addLabel(initialGeo.get());
		  } else {
			  if (addControl) {
			  	thisObj.addLabel(thisObj.geometryArray[0].get());
			  }
			  if (centerLast) {
			  	thisObj.geometryArray[0].center();
			  }
		  } 
		  $('#loadingDiv').hide();
    });
};

Map.prototype.toString = function () {
	if (this.geometryObj == null) {
		return "";
	} else {
		return this.geometryObj.toString();
	}
};

Map.prototype.geocode = function (value) {
	var address = value;
	var thisObj = this;
	if (address) {
		var geocoder = new GClientGeocoder();
		geocoder.getLatLng(address,function(point) {
		    if (!point) {
				alert(address + " not found");
		    } else {
		    	thisObj.clear();
		    	thisObj.geometryObj = new Point(thisObj.map);
		    	thisObj.geometryObj.add(point);
				//thisObj.map.setZoom(7);
				thisObj.map.panTo(point);
		    }
		  }
		);
	}
};

Map.prototype.hideCategory = function(category) {
	for (var i=0; i<this.geometryArray.length; i++) {
		if (this.geometryArray[i].getCategory() == category) {
			this.geometryArray[i].hide();
			if (this.geometryArray[i].getID() == this.label.id) {
				this.hideLabel();
			}
		}
	}
	
	/*
	// Hide category for clustering
	if (category == "CommunityOak")
		this.communityClusterer.clearMarkers();
	else
		this.officialClusterer.clearMarkers();
	*/
};

Map.prototype.showCategory = function(category) {
	for (var i=0; i<this.geometryArray.length; i++) {
		if (this.geometryArray[i].getCategory() == category) {
			this.geometryArray[i].show();
			if (this.geometryArray[i].getID() == this.label.id) {
				this.showLabel();
			}
		}
	}
	
	/*
	// Show category for clustering
	if (category == "CommunityOak")
		this.communityClusterer = 
			 new MarkerClusterer(this.map, this.communityCluster, 
					  {maxZoom: 11, styles: clusterStyles(0)});
	else
		 this.officialClusterer = 
			  new MarkerClusterer(this.map, this.officialCluster, 
					  {maxZoom: 11, styles: clusterStyles(1)});
	*/
};

Map.prototype.center = function(id) {
	for (var i=0; i<this.geometryArray.length; i++) {
		if (this.geometryArray[i].getID() == id) {
			this.geometryArray[i].center();
		}
	}
};

Map.prototype.editLoad = function(url) {
	this.submissionMode = true;
    var geo = {};
    var thisObj=this;
	GDownloadUrl(url, function(data, responseCode) {
	  var xml = GXml.parse(data);
	  var jXml = $(xml);
	  jXml.find('item').each(function(){
		geo.id =$(this).find('guid').text();
		geo.type = $(this).find('geomType').text();
		geo.submitType = $(this).find('submitType').text();
		var points= $(this).find('geomType').next().text();
	  	var latlngArray = new Array();
	    latlngArray = points.split(" ");
		if (geo.type == "POINT") {
			thisObj.geometryMode = "point";
		    var point = new GLatLng(parseFloat(latlngArray[0]),parseFloat(latlngArray[1]));
		    thisObj.geometryObj = new Point(thisObj.map);
			thisObj.geometryObj.add(point);
			thisObj.map.panTo(point);
		} else {
			thisObj.geometryMode = "polygon";
			thisObj.geometryObj = new Polygon(thisObj.map);
			for (var j=0; j<latlngArray.length-2; j++) {
			    thisObj.geometryObj.add(new GLatLng(parseFloat(latlngArray[j]),parseFloat(latlngArray[j+1])));
			    j++;
			}
			thisObj.map.panTo(thisObj.geometryObj.get().getBounds().getCenter());
		}
	  });
	});
};

Map.prototype.select = function(id) {
};

Map.prototype.scroll = function(forward) {
	if (forward) {
		if (this.index<this.geometryArray.length)
			this.index++;
	} else {
		if (this.index>0)
			this.index--;
	}
	GEvent.trigger(this.geometryArray[this.index].get(),'click');
}

/*************************************************************
 * Geometry class: Super class for all geometry classes
 *************************************************************/
function Geometry(map){
	this.map = map;
	this.geo = null;
	this.icon_url = webroot+"img/tree/";
	this.icon_add_color = "grey-circle.png";
	this.icon_community_color = "orange-circle.png";
	this.icon_official_color = "red-circle.png";
}

Geometry.prototype.showMe = function() {
	this.map.panTo(this.geo.center);
};

Geometry.prototype.draw = function() {
	this.map.addOverlay(this.geo);
};

Geometry.prototype.center = function(){
	this.map.panTo(this.geo.center);
};

Geometry.prototype.get = function() {
	return this.geo;
};

Geometry.prototype.getID = function() {
	return this.geo.id;
};

Geometry.prototype.getCategory = function(name) {
	switch (name) {
		case "type":
			return this.geo.type;
		default:
			return this.geo.submitType; 
	}
};

Geometry.prototype.hide = function() {
	this.geo.hide();
};

Geometry.prototype.show = function() {
	this.geo.show();
};

Geometry.prototype.load = function (points, info, addControl) {
	addControl = addControl || false; // default value is no control
	
	if (info.type =="POINT") {
		if (info.submitType == "CommunityOak") {
			var icon = createIcon(this.icon_url+this.icon_community_color);
		} else {
			var icon = createIcon(this.icon_url+this.icon_official_color);
		}
		this.geo = new GMarker(points, {icon:icon});
		this.geo.center = this.geo.getLatLng();
	} else {
		if (info.submitType == "CommunityOak") {
			this.geo = new GPolygon(points, this.line.color, this.line.weight, 
						this.line.opacity, this.commFill.color, this.commFill.opacity);
		} else {
			this.geo = new GPolygon(points, this.line.color, this.line.weight, 
						this.line.opacity, this.offFill.color, this.offFill.opacity);
		}
		this.geo.center = this.geo.getBounds().getCenter();
	}
	
	this.geo.id = info.id;
	this.geo.submitType = info.submitType;
	
	if (addControl) {
		var thisObj = this;
		GEvent.addListener(this.geo, "click", function() {
			// oakInfo is a callback function for display and handling the oak info pane
			$.get(webroot+'oaks/viewAjax/'+this.id, function(data){
				$('#oak-info-comments').html(data);
				$("a.addComment").click(function(){
					$("div#addComment").toggle(); 
					return false;
				});
				/*
				$('a#original').click(function() {
					jQuery.blockUI({ 
						message: '<img height="450px" src="'+$(this).attr('href')+'" />',
					    css: { 
					        padding:        '10px',
					        margin:         0,
					        width:          '70%', 
					        top:            '5%', 
					        left:           '15%', 
					        textAlign:      'center', 
					        cursor:         'default'					        
					    }
					});
					
					$('body').click(function() {
						jQuery.unblockUI();
					});
				
					return false;
				});
				*/
				$("a.login").click(function(){
				  $("div#login").slideToggle("slow");
				});    
			}); 
			thisObj.showMe();
		});
	}
};

/*************************************************************
 * Point class: Handles various methods and properties of a point(marker) on the map.
 *************************************************************/
function Point (map) {
	this.base = Geometry;
	this.base(map);
}
Point.prototype = new Geometry;

/* Function for creating a marker */
Point.prototype.add = function (point) {
	// Red marker icons
	if (this.geo) {this.clear();}
	var icon = createIcon(this.icon_url+this.icon_add_color);
	this.geo = new GMarker(point, {icon:icon});
	GEvent.bind(this.geo, 'click', this, this.clear);
	this.draw();
};

Point.prototype.clear = function() {
	if (this.geo != null) {
		this.map.removeOverlay(this.geo);
		this.geo = null;
	}
};

Point.prototype.toString = function() {
	var point_str = ""; 
	if (this.geo != null) {
		var coors = this.geo.getLatLng();
		point_str = "POINT("+coors.lng()+" "+coors.lat()+")";
	}
	return point_str;
};

/*************************************************************
 * Polygon class: Handles various methods and properties of a polygon on the map.
 *************************************************************/
function Polygon (map) {
	this.base = Geometry;
	this.base(map);
	this.markers = new Array();
	this.line = {color: "#000000", weight:1, opacity:0.8};	// Orange
	this.commFill = {color: "#ff8800",opacity: .4};				// Orange
	this.offFill = {color: "#770000",opacity: .4};				// Red
	this.addfill = {color: "#cccccc",opacity: .4};				// Grey
}
Polygon.prototype = new Geometry;

/* Function for creating a marker */
Polygon.prototype.add = function (point) {
	// Red marker icons
	var icon = createIcon(this.icon_url+this.icon_add_color);
	var marker = new GMarker(point, {icon:icon, draggable:true, bouncy:false, dragCrossMove:true});
	this.map.addOverlay(marker);
	this.markers.push(marker);
	//console.log(this.markers);
	
	var thisObj = this;	
	GEvent.addListener(marker, "drag", function() {
		thisObj.redraw();
	});

	GEvent.addListener(marker, "click", function() {
		// Find out which marker to remove
		for(var n = 0; n < thisObj.markers.length; n++) {
		 if(thisObj.markers[n] == marker) {
		  thisObj.remove(thisObj.markers[n]);
		  break;
		 }
		}
		
		// Shorten array of markers and adjust counter
		thisObj.markers.splice(n, 1);
		thisObj.redraw();
	});
	
	this.redraw();
};

// In the polygon mode, draw an overlay 
Polygon.prototype.redraw = function(){
	var points = new Array();
	if(this.geo) { this.removePolygon(); }

	if (this.markers.length > 0) {
		for(i = 0; i < this.markers.length; i++) {
			points.push(this.markers[i].getLatLng());
		}
		
		// Add the first point to the end to close the polygon
		points.push(this.markers[0].getLatLng());
		
		this.geo = new GPolygon(points, this.line.color, this.line.weight, 
							this.line.opacity, this.addfill.color, this.addfill.opacity);
		this.draw();
	}
};

Polygon.prototype.clear = function() {
	if (this.geo != null) {
		for (var i=0; i<this.markers.length; i++)
			this.map.removeOverlay(this.markers[i]);
		this.map.removeOverlay(this.geo);
		this.markers = new Array();
	}
};

Polygon.prototype.remove = function (vertex) {
	this.map.removeOverlay(vertex);
};

Polygon.prototype.removePolygon = function() {
	this.map.removeOverlay(this.geo);
};

Polygon.prototype.toString = function() {
	var points_str="";
	if (this.markers.length>2) {
		for(var i = 0; i < this.markers.length; i++) {
		 var coors = this.markers[i].getLatLng();
		 if (i > 0) {
		 	points_str = points_str + ","+coors.lng()+" "+coors.lat(); 
		 } else {
		 	points_str = coors.lng()+" "+ coors.lat();
		 }
		}
	
		var firstCoors = this.markers[0].getLatLng()
		points_str = points_str + ","+firstCoors.lng()+" "+firstCoors.lat();
		points_str = "POLYGON(("+points_str+"))";
	}
	return points_str;
};

/*************************************************************
 * Common functions after refactoring
 *************************************************************/
function createIcon (image) {
	var icon = new GIcon();
	icon.image = image;
	icon.iconSize = new GSize(16, 16);
	icon.shadowSize = new GSize(16, 16);
	icon.iconAnchor = new GPoint(8, 8);
	icon.infoWindowAnchor = new GPoint(5, 1);
	return icon;
};

function clusterStyles (type) {
    var styles = [[{
  	  url: webroot+"img/tree/orange-circle-30.png",
        height: 30,
        width: 30,
      },
      {
      	url: webroot+"img/tree/orange-circle-40.png",        	
      	height: 40,
      	width: 40,
      },
      {
      	url: webroot+"img/tree/orange-circle-50.png",        	
      	height: 50,
      	width: 50,
      }],
      [{
    	  url: webroot+"img/tree/red-circle-30.png",
          height: 30,
          width: 30,
        },
        {
        	url: webroot+"img/tree/red-circle-40.png",        	
        	height: 40,
        	width: 40,
        },
        {
        	url: webroot+"img/tree/red-circle-50.png",        	
        	height: 50,
        	width: 50,
        }]];
    return styles[type];
};