// Requires JQuery

if(!maps)
	var maps = new Object();

if(window['placemarks'] == 'undefined')
	var placemarks = new Object();

var mydebug = '';

//Object Representing a Simpleview Map
function Map(name,divname,OPTS){
	this.defaultSettings = {
		typeControl: new GHierarchicalMapTypeControl(),
		control: new GSmallZoomControl(),
		defaultLat: OPTS.CORDS.DEFAULTLAT,
		defaultLng: OPTS.CORDS.DEFAULTLNG,
		defaultZoom: OPTS.CORDS.DEFAULTZOOM,
		defaultPoint: new GLatLng( OPTS.CORDS.DEFAULTLAT,OPTS.CORDS.DEFAULTLNG),
		width:OPTS.WIDTH,
		height:OPTS.HEIGHT,
		showtypescontrols: OPTS.SHOWTYPES,
		zoomoffset: OPTS.ZOOMOFFSET || 0
	}

	this.settings = {
		typeControl: new GHierarchicalMapTypeControl(),
		control: new GSmallZoomControl(),
		defaultLat: OPTS.CORDS.DEFAULTLAT,
		defaultLng:  OPTS.CORDS.DEFAULTLNG,
		defaultZoom:  OPTS.CORDS.DEFAULTZOOM,
		defaultPoint: new GLatLng( OPTS.CORDS.DEFAULTLAT,OPTS.CORDS.DEFAULTLNG),
		width:OPTS.WIDTH,
		height:OPTS.HEIGHT,
		showtypecontrols: OPTS.SHOWTYPES,
		zoomoffset: OPTS.ZOOMOFFSET || 0		
	}
	
	this.bounds = new GLatLngBounds();
	this.name = name;
	this.gmap = null;

	this.collections = new Object();
	this.defaultCollection = null;

	this.allFilters = new Object();
	this.cancelCentering = false;
	this.divname = divname;

	//Create An Alias
	var map = this;

	this.visibleTips = new Array();

	this.sortCollection = function(collection,sortBy){
		this.collections[collection].sortBy(sortBy);
	}
	

	defaultStyle = {
		image: OPTS.ICONS["default"].IMAGE,
		shadow:  OPTS.ICONS["default"].SHADOW,
		smimage:  OPTS.ICONS["default"].SMIMAGE,
		hover:true,
		iconSize: new GSize(OPTS.ICONS["default"].ICONSIZE.HEIGHT, OPTS.ICONS["default"].ICONSIZE.WIDTH),
		shadowSize: new GSize(OPTS.ICONS["default"].SHADOWSIZE.HEIGHT, OPTS.ICONS["default"].SHADOWSIZE.WIDTH),
		iconAnchor: new GPoint(OPTS.ICONS["default"].ICONANCHOR.HEIGHT, OPTS.ICONS["default"].ICONANCHOR.WIDTH),
		infoWindowAnchor: new GPoint(OPTS.ICONS["default"].INFOWINDOWANCHOR.HEIGHT, OPTS.ICONS["default"].INFOWINDOWANCHOR.WIDTH)
	};
		
	redStyle = {
		image: imgRoot + '/includes/images/gmaps/marker_red_icon:label.png',
		shadow: imgRoot + '/includes/images/gmaps/marker_shadow.png',
		smimage: imgRoot + '/includes/images/gmaps/sm_marker_red_icon:label.png',
		hover:false,
		iconSize: new GSize(24.0, 35.0),
		shadowSize: new GSize(42.0, 35.0),
		iconAnchor: new GPoint(11.0, 34.0),
		infoWindowAnchor: new GPoint(14.0, 17.0)
	};
	
	
	this.initCustomIcons = function(){
		this.iconStyles = {
		};	
	}


	this.initIcons = function(){
		this.iconStyles = {
			'default': defaultStyle
		};
	};
	
	this.initCustomIcons = function(){
		this.iconStyles = {
			'default': defaultStyle
		};
	}
	
	//Initialize Map
	this.create = function(useClustering){
		this.initCustomIcons();
		map.div = $("#" + divname);
		var mapTypes = G_DEFAULT_MAP_TYPES;
		for (var i = 0; i < mapTypes.length; i++) {
			mapTypes[i].getMaximumResolution = function(latlng){ return 15;};
			mapTypes[i].getMinimumResolution = function(latlng){ return 1;};
		}

		map.gmap = new GMap2(map.div.get(0), {
			mapTypes: mapTypes, size: new GSize(this.settings.width,this.settings.height), backgroundColor: '#000000'
			}
		);

		if(!this.cancelCentering){
			map.gmap.setCenter(map.settings.defaultPoint);
			map.gmap.setZoom(map.settings.defaultZoom + map.settings.zoomoffset);
		}

		if(map.settings.control != null){
			map.control = map.settings.control;
			map.gmap.addControl(map.control);
		}
		if(map.settings.typeControl != null){
			if(map.settings.showtypecontrols){
				map.typeControl  = map.settings.typeControl;
				map.gmap.addControl(map.typeControl);
				map.gmap.addMapType(G_PHYSICAL_MAP);
				map.gmap.addMapType(G_SATELLITE_3D_MAP);	
			}
		}
		map.gmap.disableScrollWheelZoom();
		map.gmap.enableDragging();
		map.gmap.setMapType(G_PHYSICAL_MAP);
		this.useClustering = false;

		//Register Map with main array
		maps[map.name] = map;
	}
	
	this.removeOverlay = function(overlay){
		if(overlay.tooltip){
			this.gmap.removeOverlay(overlay.tooltip);
		}
		
		this.gmap.removeOverlay(overlay);
	}
	
	this.addCollection = function(oc,isDefault){
		this.collections[oc.name] = oc;
		if(isDefault || this.defaultCollection == null)
			this.defaultCollection = oc;
		oc.map = this;
		return oc;
	}

	//Set Current Maps Control
	this.setControl = function(control){
		if(map.settings.control)
			map.gmap.removeControl(map.settings.control);

		if(control)
			map.gmap.addControl(control);
		this.settings.control = control;
	}

	this.getBounds = function(){
		var bounds = new GLatLngBounds();
		for (var i in this.collections){
			var c = this.collections[i];
			var cbounds = c.calcBounds();
			bounds = addBounds(bounds,cbounds);
		}
		return bounds;
	}
	
	this.setBoundsCenterAndZoom = function(){
		map.bounds = this.getBounds();
		if(!map.bounds.isEmpty()){
			var p = map.bounds.getCenter();
			var z = map.gmap.getBoundsZoomLevel(map.bounds);
		}
		else{
			var p = map.settings.defaultPoint;
			var z = map.settings.defaultZoom;
		}
		map.gmap.panTo(p);
		map.gmap.setZoom(z + map.settings.zoomoffset);
	}

	this.removeOverlays = function(collection){
		for(var i in collection.data){
			var p = collection.data[i];
			if(p.marker){
				this.removeOverlay(p.marker);
			}
		}
	}

	//Remove all placemarks from the map
	this.removePlacemarks = function(collection,cancelHandlers){
		this.removeOverlays(collection);
		collection.clearAll();
		if(!cancelHandlers)
			collection.execHandler('addremove','removeplacemarks');
	}
	
	this.clearAll = function(){
		this.data = new Object();
		this.length = 0;
		this.bounds = new GLatLngBounds();
	}

	this.removePlacemark = function(placemark, collection, cancelHandlers){
		if(!collection)
			collection = this.defaultCollection;

		if(placemark.marker){
			this.removeOverlay(placemark.marker);
		}
		collection.clear(placemark);
		
		if(!cancelHandlers){
			collection.execHandler('addremove','removeplacemark');
		}
		logit('End: removePlacemark - ' + placemark.name + ' - ' + collection.name);		
	}

	//Add a Simple Marker
	this.addPlacemark = function(placemark,collection,cancelHandlers){
		if(!collection)
			collection = map.defaultCollection;

		collection.set(placemark);

		if(!cancelHandlers){
			collection.execHandler('addremove','addplacemark');
		}
	}

	this.showPlacemark = function(placemark,cancelHandlers){
		var marker = placemark.marker;
		var collection = placemark.collection;
		map.bounds.extend(marker.getPoint());
		map.gmap.addOverlay(marker);

		if(marker.tooltip){
			map.gmap.addOverlay(marker.tooltip);
			
			GEvent.addListener(marker, "mouseover", 
				function() {
					this.tooltip.show();
					map.visibleTips.push(this.tooltip);
					return false;
				}
			)
			
			GEvent.addListener(marker, "mouseout",
				function() {
					this.tooltip.hide();
					$(map.visibleTips).map(
						function(idx,itm){
							itm.hide();
						}
					);
					map.visibleTips = new Array();
					return false;
				}
			)
		}

		GEvent.addListener(marker, "click", function() {
			map.gmap.panTo(marker.getPoint());
		});

	}

	this.updatePlacemarkImage = function(placemark,type){
		var marker = placemark.marker;
		var icon = marker.getIcon();
		if(type == 'blank'){
			var newimage = icon.blankimage;
		}
		else
			var newimage = icon.pinimage;
		
		marker.setImage(newimage);
		icon.image = newimage;
	}




	this.sendBack = function(){
		if(!this.zindex)
			this.zindex = 100;
		this.zindex++;
		return this.zindex;
	}

	//Ensures all placemarks in this array have a marker created
	this.preparePlacemarks = function(placemarks,oc) {
		var marks = new Array();

		var processPlacemark = function(mark, collection, caller) {
			var placemark = mark;
			var self = caller;
			//Just trying to be careful with this one
				//Assign icon
			var iconStyle = mark.pinned ? 'pushpin' : collection.getIconStyle(mark);
			mark.iconstyle = iconStyle;
			placemark.icon = self.createCustomIcon(iconStyle, placemark.label);
				
				//Get Text for ToolTip
			placemark.tooltipText = collection.getToolTip(placemark);
			placemark.marker = new GMarker(new GLatLng(placemark.latitude,placemark.longitude),{icon:placemark.icon});
			placemark.marker.tooltip = new Tooltip(placemark.marker,placemark.tooltipText,4);
	
				//Add Reverse Lookup - Memory leak Issue?
			placemark.marker.placemark = placemark;
			placemark.marker.myInfoHTML = collection.getInfoHTML(placemark);
		}
		oc = oc || this.defaultCollection;
		marks = oc.getPage(oc.currPage);
		for (var i = 0; i < marks.length; i++) {
			processPlacemark(marks[i], oc, this);
		}
	}

	this.showPlacemarks = function(oc,page,cancelHandlers){
		this.removeOverlays(oc);
		if(!page)
			page = 1;

		oc.currPage = page;

		var placemarks = oc.getPage(page);

		//Prepare placemarks
		this.preparePlacemarks(placemarks,oc);

		//Set the center and zoom of map
		if (!this.cancelCentering){
			map.setBoundsCenterAndZoom();
		} 

		//Show them all
		for (var i = 0; i < placemarks.length; i++){
			var placemark = placemarks[i];
			if(!placemark.isClustered){
				var p = new GLatLng(placemark.latitude,placemark.longitude);
				var iconStyle = placemark.pinned ? 'pushpin' : oc.getIconStyle(placemark);
				placemark.iconstyle = iconStyle;
				placemark.icon = this.createCustomIcon(iconStyle, placemark.label);
				var m = new GMarker(new GLatLng(placemark.latitude,placemark.longitude),{icon:placemark.icon,zIndexProcess:$.shove(this.sendBack, this)});
				m.tooltip = new Tooltip(m,placemark.tooltipText,4);
				//Add Reverse Lookups
				m.placemark = placemark;
				placemark.marker = m;
				this.showPlacemark(placemark,true);
			}
		}

	}

	this.addPlacemarks = function(placemarks,collection,cancelHandlers){
		for (var i = 0; i < placemarks.length; i++){
			var g = placemarks[i];
			//Add Reverse Lookups
			map.addPlacemark(g,collection,true);
		}
	}

	this.createCustomIcon = function (style,label,useBlank){
		if (typeof(useBlank) == 'undefined')
			useBlank = true;
		var style ='default';//hack
		var pathtoblank = 'http://dev2.simpleviewinc.com/simpleview10/web/includes/images/blank.gif';
		var iconStyle = clone(this.iconStyles[style]);
		if(useBlank)
			iconStyle.shadow = pathtoblank;
		iconStyle.image = iconStyle.image.replace('icon:label',label);
		if (iconStyle.smimage) 
			iconStyle.smimage = iconStyle.smimage.replace('icon:label',label);
		var icon = new GIcon(iconStyle);
		//Various Image Types
		icon.pinimage = icon.image;
		icon.blankimage = pathtoblank;
		iconStyle.shadowimage = iconStyle.shadow;
		if(useBlank)
			icon.image = icon.blankimage;
		else
			icon.image = pinimage;
		return icon;
	}
	
	
	this.registerFilter = function(name,filter){
		this.allFilters[name] = filter;
	}
	
	this.initFilters = function(){
		for(var i in this.allFilters){
			this.allFilters[i].run();
		}
	}
	
	this.getVisiblePlacemarks = function() {
		var arr = new Array();
		for (var i in this.collections){
			var p = this.collections[i].getPage(this.collections[i].currPage);
			for(var j = 0; j < p.length; j++){
				arr.push({name:p[j].name,collection:this.collections[i].name,prikey:p[j].prikey});
			}
		}
		return arr;
	}
}



function SVToolbarControl(options) {
	this.toggleView = options.toggleView;
}

// To "subclass" the GControl, we set the prototype object to
// an instance of the GControl object
SVToolbarControl.prototype = new GControl();

// Creates a one DIV for each of the buttons and places them in a container
// DIV which is returned as our control element. We add the control to
// to the map container and return the element for the map class to
// position properly.
SVToolbarControl.prototype.initialize = function(map) {
	var tbcontainer = document.createElement("div");
	$(tbcontainer).html("<a href=\"#\" class=\"max\"><img src=\"" + imgRoot + "/includes/images/gmaps/expand.png\" alt=\"view large map\" /></a>");
	jQuery(".max", tbcontainer).click(this.toggleView);
	
	map.getContainer().append(tbcontainer);
	return tbcontainer;
}

// By default, the control will appear in the top left corner of the
// map with 7 pixels of padding.
SVToolbarControl.prototype.getDefaultPosition = function() {
  return new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(10, 10));
}



function SVNearbyControl(options) {
}

// To "subclass" the GControl, we set the prototype object to
// an instance of the GControl object
SVNearbyControl.prototype = new GControl();

// Creates a one DIV for each of the buttons and places them in a container
// DIV which is returned as our control element. We add the control to
// to the map container and return the element for the map class to
// position properly.
SVNearbyControl.prototype.initialize = function(map) {
	var container = document.createElement("div");
	$(container).html("<select name=\"categories\"><option>find nearby attractions</option></select>");
	jQuery(".max", container).click(this.toggleView);
	
	map.getContainer().append(container);
	return container;
}

// By default, the control will appear in the top left corner of the
// map with 7 pixels of padding.
SVNearbyControl.prototype.getDefaultPosition = function() {
  return new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(10, 10));
}

/*************************************************
	General Utility Functions
**************************************************/

function addBounds(b1,b2){
	if (b1.isEmpty())
		return b2;
		
	if(b2.isEmpty())
		return b1;

	var southWest = b1.getSouthWest();  
	var northEast = b1.getNorthEast();
	var b3 = new GLatLngBounds();
	b3.extend(southWest);
	b3.extend(northEast);
	var southWest = b2.getSouthWest();  
	var northEast = b2.getNorthEast();
	b3.extend(southWest);
	b3.extend(northEast);
	return b3;
}

function placemarkDistance(p1,p2){
	return distance(parseFloat(p1.latitude),parseFloat(p1.longitude),parseFloat(p2.latitude),parseFloat(p2.longitude));
}

function distance(lat1,lon1,lat2,lon2){
	var R = 6371; // km
	var kpm = 1.609344;  //km per mile
	var dLat = (lat2-lat1).toRad();
	var dLon = (lon2-lon1).toRad(); 
	var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
			Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) * 
			Math.sin(dLon/2) * Math.sin(dLon/2); 
	var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
	var d = (R * c) / kpm;
	return d;
}

function parsePlacemarkData(str,p) {
	var fieldlist = ['name','prikey','weburl','addr1','addr2','city','state','zip','phone','latitude','longitude'];
	
	for (var i = 0; i < fieldlist.length; i++){
		var searchstr = 'placemark:' + fieldlist[i];
		var replacewith = String(p[fieldlist[i]]).substr(0,100);
		while (str.indexOf(searchstr) > 0 && searchstr != replacewith){
			str = str.replace(searchstr,replacewith);
		}
	}

	var searchstr = 'placemark:description';
	if(p.description.length > 0){
		var replacewith = p.description;
	}
	else
		var replacewith = '*No Description Available*';

		while (str.indexOf(searchstr) > 0 && searchstr != replacewith){
			str = str.replace(searchstr,replacewith);
		}

	//Only available when the marker is visible on the map
	if (p.icon) {
		str = str.replace('placemark:iconimage',p.icon.image);
		str = str.replace('placemark:sm_iconimage',p.icon.smimage);
	}
	
	if (p.weburl.length > 0)
		str = str.replace('placemark:website_redirect', '<a href=\"http://' + (p.weburl.replace('http://','')) + '\" target=\"_blank\">Visit Website</a><br>');
	else
		str = str.replace('placemark:website_redirect', '');
		
	return str;
}

function chr(num){
	return String.fromCharCode(num);
}

function asc(str){
	return str.charCodeAt(0);
}

function clone(obj){
    if(obj == null || typeof(obj) != 'object')
        return obj;

    var temp = new obj.constructor(); // changed (twice)
    for(var key in obj)
        temp[key] = clone(obj[key]);

    return temp;
}

Number.prototype.toRad = function() {  // convert degrees to radians
  return this * Math.PI / 180;
}

var tabdepth = 0;

function logit(str){
	return true;
	
	if (typeof(console) !== 'undefined' && console != null) {
		var outstr = str;
		if (str.indexOf('End') == 0) {
			tabdepth -= 1;
		}
		for (var i = 0; i < tabdepth; i++) {
			outstr = '--' + outstr;
		}
		if (console) 
			console.info(outstr);
		if (str.indexOf('Called') == 0) {
			tabdepth += 1;
		}
	}else{
		return false; //Will have to do an IE version of this 
	}
}

//Cleanup to mitigate memory leaks resulting from cicular dom references and use of closures
$(window).unload(function(){GUnload();});

// jquery adaptation for Function.bind- http://www.quirkey.com/blog/2009/02/25/switched-to-jquery/
$.extend({
  shove: function(fn, object) {
    return function() {
      return fn.apply(object, arguments);
    }
  }
});
