(function($){
	$.fn.mngmap = function(options) {
		
		/* 
		** ********************
		** Defaults
		*/
		var defaults = {
			'initLat': 40.740161,
			'initLng': -73.985879,
			'initZoom': 12,
			'maxAutoZoomLevel': 15,
			'showCategoryController': true/*,
			'json': 'http://tregnydev.com/action/blog_address',
			'jsoninline': 'blog_addres'*/
		};
		var options = $.extend(defaults, options);
		
		
		/*
		** ********************
		** Variable declarations
		** CONTROLS object: contains all custom control DOM elements needed for jQ manipulation
		*/
		_DOMCONTROLS = { 'cats': null, 'tip': null, 'tipinner': null };
		mm_array = {}; // MarkerManager array	
		markerGroups = {}; // unused at moment...	
		allmarkers = [];
		usedcategories_arr = [];
		
		var customIcons = [];
			customIcons['Notable Buildings'] = 'bldg';
			customIcons['New Developments'] = 'bldg';
			customIcons['Restaurants & Food'] = 'food';
			customIcons['Restaurants and Food'] = 'food'; // temporary fix
			customIcons['Shopping'] = 'shopping';
			customIcons['Nightlife'] = 'nightlife';
			customIcons['Attractions & Events'] = 'attractions';
			customIcons['Hotels'] = 'hotels';
			customIcons['Schools & Education'] = 'schools';
			customIcons['Parks'] = 'parks';
			customIcons['Activities & Exercise'] = 'religious'; // TEMPORARY FIX!
			customIcons['Transportation'] = 'transportation';
			customIcons['Religious'] = 'religious';
			customIcons['Uncategorized'] = 'blank';
		
		
		/*
		** ********************
		** CategoryFilterControl: prototyped Gmap control
		*/
		function CategoryFilterControl() {};
		
		CategoryFilterControl.prototype = new GControl();
		
		CategoryFilterControl.prototype.initialize = function(map) {
			var cats = document.createElement('div');
			_DOMCONTROLS.cats = cats;
		
			$(cats).css({
				'font-size': '1.1em',
				'font-family': 'Arial, Helvetica, sans-serif',
				'background': 'url(' + TREGNYURL + '/css/img/i/15x15_fffsemitrans.png) 0 0 repeat',
				'padding': '3px 5px',
				'border': '1px solid #aaa',
				'border-top': 'none',
				'border-right': 'none',
				'margin': '0 0 5px'
			});

			// hide if requested
			if (!options.showCategoryController) $(cats).css({'display': 'none'});
		
			$(cats).append('<span id="CFC_loading" style="font-weight:bold">Loading...</span>');
			map.getContainer().appendChild(cats);
			return cats;
		}

		CategoryFilterControl.prototype.getDefaultPosition = function() {
			return new GControlPosition( G_ANCHOR_TOP_RIGHT );
		}
		
		
		/*
		** ********************
		** TooltipControl: prototyped Gmap control
		*/
		function TooltipControl() {};
		
		TooltipControl.prototype = new GControl();
		
		TooltipControl.prototype.initialize = function(map) {
			var tip = document.createElement('div');
			var tip_inner = document.createElement('div');
			
			_DOMCONTROLS.tip = tip;
			_DOMCONTROLS.tipinner = tip_inner;
			
			$(tip).css({
				'padding': '0 6px 6px 0',
				'background': 'url(/js/plugins/jquery.mngmap.shadow.png) bottom right no-repeat',
				'display': 'none',
				'position': 'relative',
				'width': '153px'
			});
			
			$(tip_inner).css({
				'font-size': '0.9em',
				'width': '140px',
				'background': '#fff url(/js/plugins/jquery.mngmap.bottom-left.gif) bottom left no-repeat',
				'padding': '4px 6px',
				'border-left': '1px solid #ddd',
				'border-top': '1px solid #ddd'
			});
			
			tip.appendChild(tip_inner);
			map.getContainer().appendChild(tip);
			return tip;
		};
		
		TooltipControl.prototype.getDefaultPosition = function() {
			return new GControlPosition(G_ANCHOR_TOP_LEFT);
		}
		
		
		
		
		
		
		/*
		** ********************
		** function chopName
		*/
		function chopName(name) {
			var namearr = name.split(' ');
			namearr.pop(); // remove 'Guide'
			namearr.pop(); // remove 'Neighborhood'
			return namearr.join(' ');
		}
		
		/* function makeIcon
		** @ give it a filename w/o extension -> GIcon
		*/
		function makeIcon(name) {
			var filename = (customIcons[name]) ? customIcons[name] : customIcons['Uncategorized'];
			return new GIcon({
											 'image': 'http://www.tregny.com/css/img/i/mngico/'+filename+'.png',
											 'shadow': 'http://www.tregny.com/css/img/i/mngico/shadow.png',
											 'iconSize': new GSize(25, 29),
											 'iconAnchor': new GPoint(12, 27),
											 'infoWindowAnchor': new GPoint(11, 3)
											 });
		}
			
	
		/* function createMarker
		** creates a marker
		*/
		function createMarker(point, title, url, hood, categories) {
		
			var cat_for_icon = categories[0]; // eventually might need better method for icon selection...
			var marker = new GMarker(point, {icon: makeIcon(cat_for_icon)}); // remove title: title; we don't want tooltips
			
			
			//var isNewDev = false;
			for(var x=0; x<categories.length; x++) {
				markerGroups[categories[x]].push(marker);
				//if(categories[x] == 'New Developments'){ isNewDev = true; }
			};
			//if(!isNewDev){ marker.pleaseHideLater = true; }
			//isNewDev = false;
			
			
			allmarkers.push(marker);
			//var markerHTML = '<a href="'+url+'" style="font-size:1.2em;font-weight:bold;">'+title+'</a><br>'+hood+'<br>Under: '+categories.join(', ');
			var markerHTML = '<span style="font-size:1em;line-height:1em;font-weight:bold;display;block;margin:0 0 2px;">'+title+'</span><span style="font-family: Arial, Helvetica, sans-serif;display:block;">'+hood+'</span><span style="font-family:Arial, Helvetica, sans-serif;display:block;">Under: '+categories.join(', ')+'</span>';
			
			GEvent.addListener(marker, 'mouseover', function(){ Tooltip('show', marker, markerHTML); });
			GEvent.addListener(marker, 'mouseout', function(){ Tooltip('hide'); });
			GEvent.addListener(marker, 'click', function(){ window.location.href = url; });
			
			return marker;
		}
	
		/* function createMarker
		** creates a marker
		*/
		function updateMapByFilters(arr) {
			var cats_to_show = [];
			
			// Clear markers
			for(var x=0; x<allmarkers.length; x++) {
				allmarkers[x].hide();
				allmarkers[x].pleaseHideLater = true;
			}
		
			if ($('.filterA').is(':checked')) { // is at least one checked?
			
				$('.filterA').each(function() {
					var e = $(this);
					var eltnum = e.attr('id').split('_')[1]; // eg. filtercat_0 -> 0
					
					var cat = arr[eltnum]; // contains the category associated with current checkbox element
					
					if ($(this).is(':checked')) cats_to_show.push(arr[eltnum]);
				});
				
				for (var x=0; x<cats_to_show.length; x++) {
					for (marker in markerGroups[cats_to_show[x]]) {
						markerGroups[cats_to_show[x]][marker].show();
						markerGroups[cats_to_show[x]][marker].pleaseHideLater = false;
					};
				}; // all points should be visible now...
			
			} else { // show all markers
				for (var x=0; x<allmarkers.length; x++) {
					allmarkers[x].show();
					allmarkers[x].pleaseHideLater = false;
				};
			};
		};
	
	
		/* function generateBoundsFromMarkers
		** creates a marker
		*/
		function generateBoundsFromMarkers(markers) {
			var left, right, top, bottom;
			
			for (var x=0; x<markers.length; x++) {
				if (markers[x] != '') {
					var mpos = markers[x].getLatLng();
					if (mpos.lng()<left || !left) left = mpos.lng();
					if (mpos.lng()>right || !right) right = mpos.lng();
					if (mpos.lat()<bottom || !bottom) bottom = mpos.lat();
					if (mpos.lat()>top || !top) top = mpos.lat();
				};
			};

			var sw = new GLatLng(bottom, left);
			var ne = new GLatLng(top, right);

			return new GLatLngBounds(sw, ne);
		};
		
		/* Tooltip */
			function Tooltip(showorhide, marker, html) {
				var $tip = $( _DOMCONTROLS.tip );
				var $tipinner = $( _DOMCONTROLS.tipinner );
								
				if (showorhide == 'hide') {
					$tip.hide();
				}	else {
					
					$tipinner.html(html);
					
					var _mpos = map.fromLatLngToContainerPixel(marker.getLatLng());
					
					// IE BUGS: .css('border-XXX-width') returns 'medium' instead of pixels
					// SOLUTION: hard code border widths, for now...later, set as options?
					/*var _twidth = $tip.width() + parseInt($tip.css('border-left-width')) + parseInt($tip.css('border-right-width')) + parseInt($tip.css('padding-left')) + parseInt($tip.css('padding-right'));
					var _theight = $tip.height() + parseInt($tip.css('border-top-width')) + parseInt($tip.css('border-bottom-width')) + parseInt($tip.css('padding-top')) + parseInt($tip.css('padding-bottom'));*/
					
					var _twidth = $tip.width() + 7;
					var _theight = $tip.height() + 7;
					
					var _mwidth = map.getSize().width;
					var _mheight = map.getSize().height;

					
					var TOP_RIGHT = new GPoint(12, -19);
					var TOP_LEFT = new GPoint(-2, -20);
					var BOTTOM_RIGHT = new GPoint(10, -5);
					var BOTTOM_LEFT = new GPoint(-5, -5);
					
					var ROOM_TOP = false, ROOM_RIGHT = false, ROOM_BOTTOM = false, ROOM_LEFT = false;
					
					if (_mpos.y + TOP_RIGHT.y - _theight >= 0) ROOM_TOP = true;
					if (_mpos.x + TOP_RIGHT.x + _twidth <= _mwidth) ROOM_RIGHT = true;
					if (_mpos.y + BOTTOM_LEFT.y + _theight <= _mheight) ROOM_BOTTOM = true;
					if (_mpos.x + BOTTOM_LEFT.x - _twidth >= 0) ROOM_LEFT = true;
					
					var MODE = (ROOM_TOP && ROOM_RIGHT) ? 'topright' : ( (ROOM_TOP && ROOM_LEFT) ? 'topleft' : ( (ROOM_RIGHT && ROOM_BOTTOM) ? 'bottomright' : 'bottomleft' ));
					
					if (MODE == 'topright') {
						$tipinner.css({ 'background': '#fff url(/js/plugins/jquery.mngmap.bottom-left.gif) bottom left no-repeat' });
						$tip.css({'left': (_mpos.x + TOP_RIGHT.x), 'top': (_mpos.y + TOP_RIGHT.y - _theight)}).show();
						
					} else if (MODE == 'topleft') {
						$tipinner.css({ 'background': '#fff url(/js/plugins/jquery.mngmap.bottom-right.gif) bottom right no-repeat' });
						$tip.css({ 'left': (_mpos.x + TOP_LEFT.x - _twidth),
											 'top': (_mpos.y + TOP_LEFT.y - _theight)
										 }).show();
					
					} else if (MODE == 'bottomright') {
						$tipinner.css({ 'background': '#fff url(/js/plugins/jquery.mngmap.top-left.gif) top left no-repeat' });
						$tip.css({ 'left': (_mpos.x + BOTTOM_RIGHT.x), 'top': (_mpos.y + BOTTOM_RIGHT.y) }).show();
						
					} else {
						$tipinner.css({ 'background': '#fff url(/js/plugins/jquery.mngmap.top-right.gif) top right no-repeat' });
						$tip.css({ 'left': (_mpos.x + BOTTOM_LEFT.x - _twidth), 'top': (_mpos.y + BOTTOM_LEFT.y) }).show();
					}
				}
			} // end Tooltip
			
			
			
			/*
			 * Helper functions used by JSON parsers (getJSON and inline method)
			*/
			
			// sets up MarkerManagers for each category in 'usedcategories'
			function setupMarkerManagers( usedcategories ) {
				for (x in usedcategories) {
					usedcategories_arr.push(usedcategories[x].name);
					markerGroups[usedcategories[x].name] = [];
					mm_array[usedcategories[x].name] = new MarkerManager(map);
				};
			};
			
			function setupAndCreateMarkers( items ) {
				var ct = 0;
				for (x in items) {
					ct++;
					var cur = items[x];
					var blogname = cur.blog_name;
					var hood = chopName(blogname);
					var title = cur.post_title;
					var url = cur.post_url;
					var point = new GLatLng(cur.geocodes.lat, cur.geocodes.lng);
					var marker = createMarker(point, title, url, hood, cur.categories);
				}
			};
			


	
			
		/*
		** ********************
		** BEGIN
		*/
		return this.each(function() {  
			
			var me = this;

			if (GBrowserIsCompatible()) {
				
				// Set up the map, center on default points and zoom				
				map = new GMap2(me);
				map.addControl(new GSmallMapControl());
				map.addControl(new CategoryFilterControl());
				map.addControl(new TooltipControl());
				
				map.setCenter(new GLatLng(options.initLat, options.initLng), options.initZoom);
				map.enableDoubleClickZoom();
				mgr = new MarkerManager(map, {trackMarkers:false});
				
				// Now let's determine how we're getting JSON data
				
				/*if ( options.json.indexOf('http:') != -1 || options.json.indexOf('/') != -1 ) {
					// JSON = from URL
					jsonData = 
				} else {
					// JSON = from variable (assumption)
				};*/
				
				if ( options.jsoninline ) {
					
					setupMarkerManagers( window[options.jsoninline].usedcategories );
					setupAndCreateMarkers ( window[options.jsoninline].item );
					var gcc = _DOMCONTROLS.cats; // = custom controller container object
					
					if (allmarkers.length > 0) {
						var bounds = generateBoundsFromMarkers(allmarkers);

						var autozoomlevel = (map.getBoundsZoomLevel(bounds) > options.maxAutoZoomLevel) ? options.maxAutoZoomLevel : map.getBoundsZoomLevel(bounds);
						map.setCenter(bounds.getCenter(), autozoomlevel);
					
						mgr.addMarkers( allmarkers, 5 );
						mgr.refresh();
						
						$(gcc).append('<span class="cats-infospan">Filter by Category <em style="color:#246b9d">(show)</em></span>');
					}	else {
						$(gcc).append('<span class="cats-infospan">No posts to display</span>');
					};
					
					$gcc_innerholder = $('<div style="display:none;">');
					$(gcc).append($gcc_innerholder);
					
					// Category filter links
					for (x in usedcategories_arr) {
						var postcount = markerGroups[usedcategories_arr[x]].length;
						var e = '<label for="filtercat_'+x+'" style="float:left;clear:left;margin:0 3px 3px 0;"><input class="filterA" type="checkbox" id="filtercat_'+x+'" style="width:15px;" /><span style="margin-left:2px;">'+usedcategories_arr[x]+' <span style="font:0.9em Arial, Helvetica, sans-serif;color:#666;">('+postcount+')</span></span></label>';
						//$('#mng_map1').before(e);
						
						// TESTING
						$gcc_innerholder.append(e);
					}
					
					$('.cats-infospan', gcc).bind('click', function(){				
						if ($gcc_innerholder.is(':hidden')) {
							$gcc_innerholder.show();
							$(this).css('margin-bottom', '3px').find('em').html('(hide)');
						} else {
							$gcc_innerholder.hide();	
							$(this).css('margin-bottom', '0').find('em').html('(show)');
						}
					});
										
					$('#CFC_loading').hide();
					
					// Bind filters to show/hide actions
					$('.filterA').each(function(i){
						$(this).bind('click', function(){
							updateMapByFilters(usedcategories_arr);
							if ($(this).is(':checked')) $(this).parent('label').addClass('labelspanred'); else $(this).parent('label').removeClass('labelspanred');
							
						});
					});
					
					
				} else {

					$.getJSON(options.json, function(data) {
	
						setupMarkerManagers( data.usedcategories );
						setupAndCreateMarkers ( data.item );
						
						var gcc = _DOMCONTROLS.cats; // = custom controller container object
						
						if (allmarkers.length > 0) {
							var bounds = generateBoundsFromMarkers(allmarkers);
							var autozoomlevel = (map.getBoundsZoomLevel(bounds) > options.maxAutoZoomLevel) ? options.maxAutoZoomLevel : map.getBoundsZoomLevel(bounds);
							
							map.setCenter(bounds.getCenter(), autozoomlevel);
						
							mgr.addMarkers( allmarkers, 5 );
							mgr.refresh();
							
							$(gcc).append('<span class="cats-infospan">Filter by Category</span>');
						}	else {
							$(gcc).append('<span class="cats-infospan">No posts to display</span>');
						};
						
						$gcc_innerholder = $('<div style="display:none;">');
						$(gcc).append($gcc_innerholder);
						
						// Category filter links
						for (x in usedcategories_arr) {
							var postcount = markerGroups[usedcategories_arr[x]].length;
							var e = '<label for="filtercat_'+x+'" style="float:left;clear:left;margin-right:3px;"><input class="filterA" type="checkbox" id="filtercat_'+x+'" style="width:15px;"  /><span style="margin-left:2px;">'+usedcategories_arr[x]+' <span style="font:0.9em Arial, Helvetica, sans-serif;color:#666;">('+postcount+')</span></span></label>';
							//$('#mng_map1').before(e);
							
							// TESTING
							$(gcc).append(e);
							
						}
						
						$('.cats-infospan', gcc).bind('click', function(){				
							if ($gcc_innerholder.is(':hidden')) {
								$gcc_innerholder.show();
								$(this).css('margin-bottom', '3px').find('em').html('(hide)');
							} else {
								$gcc_innerholder.hide();	
								$(this).css('margin-bottom', '0').find('em').html('(show)');
							}
						});
						
						$('#CFC_loading').hide();
						
						// Bind filters to show/hide actions
						$('.filterA').each(function(i){
							$(this).bind('click', function(){
								updateMapByFilters(usedcategories_arr);
								if ($(this).is(':checked')) $(this).parent('label').addClass('labelspanred'); else $(this).parent('label').removeClass('labelspanred');
								
							});
						});
						
					}); // end JSON

				}; // end if ( options.jsoninline )
			};

		});   // end each
	};  
	
})(jQuery); 

$(document).ready(function() {
	$('#filtercat_1').attr('checked', true);
});
