if (!usm) {
	var usm = {};
}
usm.staticMap = (function (that) {
	'use strict';
		//Dependancies
	var	gMap = google.maps,
		//Map closure
		Map = function (options) {
			//options = {mapDiv,position,zoom}
			var	map,
				mapDiv = document.getElementById(options.mapDiv),
				mapStyle,
				mapOptions;
			if (mapDiv !== 'undefined' && mapDiv !== null) {
				mapOptions = {
					mapTypeId: gMap.MapTypeId.ROADMAP,
					mapTypeControl: false,
					navigationControl: false,
					disableDoubleClickZoom: true,
					scrollwheel: false,
					draggable: false,
					streetViewControl: false,
					mapTypeControlOptions: {style: gMap.MapTypeControlStyle.DEFAULT}
				};
				if (options.zoom) {
					mapOptions.zoom = options.zoom;
				} else {
					mapOptions.zoom = 7;
				}
				if (options.position) {
					mapOptions.center = new gMap.LatLng(options.position[0], options.position[1]);
				} else {
					mapOptions.center = new gMap.LatLng(42.09822241118974, -70.9991455078125);
				}
				if (options.scrollable) {
					mapOptions.scrollwheel = true;
					mapOptions.draggable = true;
					mapOptions.mapTypeControl = true;
					mapOptions.navigationControl = true;
					mapOptions.disableDoubleClickZoom = false;
				}
				if (options.style) {
					mapStyle = new gMap.StyledMapType(options.style, {name: "custom_map"});
				}
				//Create map style
				if (mapStyle) {
					mapOptions
						.mapTypeControlOptions
						.mapTypeIds = [mapStyle, 'custom_map'];
				}

				map = new gMap.Map(mapDiv, mapOptions);

				//Set map style
				if (mapStyle) {
					map.mapTypes.set('custom_map', mapStyle);
					map.setMapTypeId('custom_map');
				}

				this.map = map;
				this.mapDiv = mapDiv;
			}
			this.polygons = [];
			this.markers = [];
			this.infoWindows = [];
		},
		Polygon = function (map, options) {
			//Polygon style options
			this.fillOpacity = 0.25;
			this.activeOpacity = 0.8;
			this.hoverOpacity = 0.5;
			this.strokeWeight = 2.0;
			this.strokeOpacity = 1;
			this.strokeColor = '#fff';
			this.fillColor = '#bbb';
			this.hoverText = '';

			//Apply all insignificant options
			this.applyOptions(options);

			this.useOpacity = '';

			//Active/inactive bool
			this.state = true;

			//Parse polygon path.
			if (options.path) {
				this.parsePath(options.path);
			} else {
				throw 'No path supplied for polygon';
			}

			//Dom objects
			if (options.hasOwnProperty('id')) {
				this.id = options.id;
				this.label = $('#' + this.id);
				if (!this.label.length) {
					throw 'Provided id (' + this.id + ') is malformed. No DOM object found.';
				}
				this.hoverlabel = [];
				this.checkbox = [];
			} else {
				throw 'The "id" option must be supplied.';
			}

			//Create polygon
			this.createPolygon(map);

			//Determine if label is associated with a check box
			this.getCheckBox();

			//Check status of check box.
			this.checkStatus();

			//Add hover label to DOM
			this.addHoverLabel();

			//Apply listeners to polygons and DOM objects
			this.addListeners();
		},
		icon = new gMap.MarkerImage(
			'http://d.usmre.com/i/map-search/house.png',
			new gMap.Size(16, 14),
			new gMap.Point(0, 3),
			new gMap.Point(8, 15)
		),
		iconShadow = new gMap.MarkerImage(
			'http://d.usmre.com/i/map-search/house.png',
			new gMap.Size(28, 14),
			new gMap.Point(37, 3),
			new gMap.Point(8, 15)
		),
		iconActive = new gMap.MarkerImage(
			'http://d.usmre.com/i/map-search/house.png',
			new gMap.Size(20, 20),
			new gMap.Point(17, 0),
			new gMap.Point(10, 17)
		);

	//Map prototype methods.
	Map.prototype.setBounds = function () {
		var	i = 0,
			l = this.markers.length,
			ll,
			lat = 0,
			lng = 0,
			sw,
			ne,
			bounds = [];

		for (i; i < l; i += 1) {
			ll = this.markers[i].getPosition();
			lat = ll.lat();
			lng = ll.lng();
			if (lat < bounds[0] || !bounds[0]) {//s
				bounds[0] = lat;
			}
			if (lng < bounds[1] || !bounds[1]) {//w
				bounds[1] = lng;
			}
			if (lat > bounds[2] || !bounds[2]) {//n
				bounds[2] = lat;
			}
			if (lng > bounds[3] || !bounds[3]) {//e
				bounds[3] = lng;
			}
		}
		sw = new gMap.LatLng(bounds[0], bounds[1]);
		ne = new gMap.LatLng(bounds[2], bounds[3]);
		bounds = new gMap.LatLngBounds(sw, ne);
		this.map.fitBounds(bounds);
		return this;
	};
	Map.prototype.setZoom = function (zoom) {
		var map = this.map,
			listener = gMap.event.addListenerOnce(map, "idle", function() { 
				map.setZoom(zoom);
			});
		return this;
	};
	//Method adds a polygon object to the map.
	Map.prototype.addPolygon = function (options) {
		var polygon = new Polygon(this.map, options);
		this.polygons.push(polygon);
		return this;
	};
	//Add multiple polygons at once, accepts unlimited polygonOptions objects as parameters.
	Map.prototype.addPolygons = function () {
		var i = 0,
			l = arguments.length;
		for (i; i < l; i += 1) {
			this.addPolygon(arguments[i]);
		}
	};
	//Method adds a marker to the map
	Map.prototype.addMarker = function (options) {
		var	that = this,
			position = new gMap.LatLng(options.position[0], options.position[1]),
			marker = new gMap.Marker({map: that.map}),
			markerOptions = {
				position: position,
				icon: icon,
				shadow: iconShadow
			},
			content;

		marker.setOptions(markerOptions);
		that.addInfoWindow(marker, options);
		that.markers.push(marker);

		return that;
	};
	Map.prototype.addInfoWindow = function (marker, options) {
		if (options.content) {
			var	that = this,
				content = document.createElement('div'),
				infoWindow,
				infoWindowOptions;
			content.className = 'info-window';
			content.innerHTML += options.content;
			infoWindow = new gMap.InfoWindow();

			if (options.directions) {
				content.innerHTML += '<p>Directions: <a href="#" class="todr">to</a> - <a href="#" class="fromdr">from</a></p>';
				$(content)
					.find('a.todr:eq(0)')
					.click(function () {
						that.toDirections(infoWindow, content, options.position[0], options.position[1]);
						return false;
					})
					.end()
					.find('a.fromdr:eq(0)')
					.click(function () {
						that.fromDirections(infoWindow, content, options.position[0], options.position[1]);
						return false;
					});
			}
			infoWindowOptions = {
				content: content
			};
			infoWindow.setOptions(infoWindowOptions);
			gMap.event.addListener(marker, 'mouseover', function () {
				marker.setIcon(iconActive);
			});
			gMap.event.addListener(marker, 'mouseout', function () {
				marker.setIcon(icon);
			});
			gMap.event.addListener(marker, 'click', function () {
				var i = 0,
					l = that.infoWindows.length;
				for (i; i < l; i += 1) {
					that.infoWindows[i].close();
				}
				infoWindow.open(that.map, marker);
			});
			that.infoWindows.push(infoWindow);
		}
	};
	//Add multiple markers at once, accepts unlimited markerOptions objects as parameters.
	Map.prototype.addMarkers = function () {
		var i = 0,
			l = arguments.length;
		for (i; i < l; i += 1) {
			this.addMarker(arguments[i]);
		}
	};
	//To directions
	Map.prototype.toDirections = function (iwindow, content, lat, lng) {
		var form = $(content).find('form:eq(0)'),
			formContent = '<p>Start address: (include addr, city st/region)<br /><input type="text" name="saddr" /><input type="hidden" name="daddr" value="' + lat + ',' + lng + '" /><input type="submit" class="button small" value="Get Directions" /></p>';
		if (form[0]) {
			$(form).html(formContent);
		} else {
			$(content).append('<form action="http://maps.google.com/maps/" method="GET" target="_blank" class="directions">' + formContent + '</form>');
		}
		iwindow.setContent(content);
	};
	//From directions
	Map.prototype.fromDirections = function (iwindow, content, lat, lng) {
		var form = $(content).find('form:eq(0)'),
			formContent = '<p>End address: (include addr, city st/region)<br /><input type="text" name="daddr" /><input type="hidden" name="saddr" value="' + lat + ',' + lng + '" /><input type="submit" class="button small" value="Get Directions" /></p>';
		if (form[0]) {
			$(form).html(formContent);
		} else {
			$(content).append('<form action="http://maps.google.com/maps/" method="GET" target="_blank" class="directions">' + formContent + '</form>');
		}
		iwindow.setContent(content);
	};

	Map.prototype.listenShowHide = function (show, hide, hideByDefault) {
		var that = this,
			showhide = usm.cookie.get("gmapshowhide");
		if (!show || !hide) {
			throw 'usm.staticmap.listenShowHide(): Mising selector argument.';
		}

		$(hide).click(function () {
			that.hideGmap(show, hide);
			return false;
		});
		$(show).click(function () {
			that.showGmap(show, hide);
			return false;
		});

		//Apply idle listener. If gmap is hidden before initiation then it will fail. This allows us to hide after init.
		gMap.event.addListenerOnce(this.map, 'idle', function () {
			//Default state
			if (showhide === "hide") {
				that.hideGmap(show, hide);
			} else if (showhide === "show") {
				that.showGmap(show, hide);
			} else {
				if (hideByDefault) {
					that.hideGmap(show, hide);
				} else {
					that.showGmap(show, hide);
				}
			}
		});
	};

	Map.prototype.showGmap = function (show, hide) {
		if (!show || !hide) {
			throw 'usm.staticmap.showGmap(): Mising selector argument.';
		}
		$(this.mapDiv).slideDown();
		$(hide).show();
		$(show).hide();
		usm.cookie.set('gmapshowhide', 'show', '120');
	};

	Map.prototype.hideGmap = function (show, hide) {
		if (!show || !hide) {
			throw 'usm.staticmap.hideGmap(): Mising selector argument.';
		}
		$(this.mapDiv).slideUp();
		$(hide).hide();
		$(show).show();
		usm.cookie.set('gmapshowhide', 'hide', '120');
	};

	//Polygon prototype methods.
	Polygon.prototype.createPolygon = function (map) {
		this.poly = new gMap.Polygon({
			map: map,
			strokeColor: this.strokeColor,
			fillColor: this.fillColor,
			fillOpacity: this.fillOpacity,
			strokeOpacity: this.strokeOpacity,
			strokeWeight: this.strokeWeight,
			path: this.path,
			clickable: true
		});
	};
	//true or false
	Polygon.prototype.checkStatus = function () {
		if (this.checkbox.length) {
			if (this.checkbox.attr("checked") || false) {
				this.state = false;
				this.poly.setOptions({fillOpacity: this.activeOpacity});
				this.useOpacity = this.activeOpacity;
			} else {
				this.state = true;
				this.poly.setOptions({fillOpacity: this.fillOpacity});
				this.useOpacity = this.fillOpacity;
			}
		}
	};
	Polygon.prototype.applyOptions = function (o) {
		var i;
		for (i in this) {
			if (this.hasOwnProperty(i) && o[i]) {
				this[i] = o[i];
			}
		}
	};
	Polygon.prototype.parsePath = function (path) {
		var	i = 0,
			l = path.length,
			objArray = [];
		for (i; i < l; i += 1) {
			if (Object.prototype.toString.call(path[i]) === '[object Array]') {
				objArray.push(new gMap.LatLng(path[i][0], path[i][1]));
			}
		}
		this.path = objArray;
	};
	Polygon.prototype.selectSearch = function () {
		if (this.label.is('a')) {
			location.href = this.label.attr('href');
		}
	};
	Polygon.prototype.triggerCheckBox = function () {
		if (this.checkbox.length) {
			if (this.checkbox.attr("checked")) {
				this.checkbox.attr("checked", false);
			} else {
				this.checkbox.attr("checked", true);
			}
		}
	};
	Polygon.prototype.mouseMove = function () {
		if (this.state) {
			this.poly.setOptions({fillOpacity: this.hoverOpacity});
		}
	};
	Polygon.prototype.mouseOut = function () {
		if (this.state) {
			this.poly.setOptions({fillOpacity: this.fillOpacity});
		}
	};
	Polygon.prototype.getCheckBox = function () {
		var forVal = this.label.attr('for');
		if (forVal) {
			this.checkBox = $('#'.forVal);
		} else {
			this.checkBox = this.label.find('input:eq(0)');
		}
	};
	Polygon.prototype.addHoverLabel = function () {
		if (this.hoverText) {
			$('body').append('<div id="' + this.id + 'hoverlabel" class="hoverlabel">' + this.hoverText + '</div>');
			this.hoverlabel = $('#' + this.id + 'hoverlabel');
		}
	};
	Polygon.prototype.addListeners = function () {
		var that = this;

		//Google listeners
		gMap.event.addListener(this.poly, 'mousemove', function () {
			that.label.addClass("hover");
			if (that.hoverlabel.length) {
				that.hoverlabel.css("left", usm.mousePosition.tempX + 10).css("top", usm.mousePosition.tempY + 20);
			}
			that.mouseMove();
		});
		gMap.event.addListener(this.poly, 'mouseout', function () {
			that.label.removeClass("hover");
			if (that.hoverlabel.length) {
				that.hoverlabel.css("left", -9999).css("top", -9999);
			}
			that.mouseOut();
		});
		gMap.event.addListener(this.poly, 'click', function () {
			if (that.state) {
				that.state = false;
				that.poly.setOptions({fillOpacity: 0.8});
			} else {
				that.state = true;
				that.poly.setOptions({fillOpacity: 0.5});
			}
			if (that.label.is('a')) {
				that.selectSearch();
			} else if (that.checkbox.is('input') || false) {
				that.triggerCheckBox();
			}
		});

		//Add hover and click states.
		this.label.hover(function () {
			that.mouseMove();
		}, function () {
			that.mouseOut();
		}).click(function () {
			that.checkStatus();
		});

		//Change status for check box.
		if (this.checkBox.length) {
			this.checkbox.change(function () {
				that.checkStatus();
			});
		}
	};

	that.createMap = function (options) {
		var map = new Map(options);
		that.maps.push(map);
		return map;
	};

	that.getIcon = function () {
		return [icon, iconShadow, iconActive];
	};

	that.maps = [];

	return that;
}(this));
