Hash.prototype.get = Hash.prototype.get.wrap(function(proceed, key) {
  return this[key] || proceed(key)
})
Hash.prototype.set = Hash.prototype.set.wrap(function(proceed, key, value) {
  this[key] = value
  return proceed(key, value)
})
Hash.prototype.initialize = Hash.prototype.initialize.wrap(function(proceed, object) {
  proceed(object)
  Object.extend(this, this._object)
})
Hash.prototype._each = Hash.prototype._each.wrap(function(proceed, iterator) {
  for (key in this) {
    if (key != '_object' && this[key] !== this._object[key] && this[key] !== Hash.prototype[key])
      this._object[key] = this[key];
  }
  proceed(iterator)
})


// info window templates
var templates = {
    cluster: new Template('<div class="tiptext">There are #{composite_count} locations in this general area.</div><div class="zoom"><a onclick="clusterZoom({lat:#{lat},lng:#{lng}}, #{zoom});return false;" href="#">Zoom in</a> to see individual listings</div>'),
    address: new Template('#{street}<br/>#{city}, #{state} #{zip}'),
    tab_link: new Template('<a href="#" onclick="category_tabs.setActiveTab(\'cat_#{id}\');">#{name}</a>&nbsp;|&nbsp;'),
    driving_directions: new Template('<a href="' + driving_directions_url +  '?address_from=#{address_from_string}&address_to=#{address_string}&map_center_start=#{lat},#{lng}" onclick="return Popwindow.open({url:this.href});">Get Directions</a>'),
	single: new Template('<div class="mapInfoWindow"><table cellpadding="0" cellspacing="0"><tr><td class="infoWindowHead" colspan="2"></td></tr><tr><td class="infoWindowLeft" >#{title}<br/>#{address}<br/>#{distance}<br/></td><td class="infoWindowRight" >#{contact}<br/>#{description}<br/>#{driving_directions}<br/></td></tr></table></div>'),
    multi_office: new Template('<div class="mapInfoWindow"><table cellpadding="0" cellspacing="0"><tr><td class="infoWindowHead">#{title}</td></tr><tr><td class="infoWindowTop">#{address}<br/><br/><div style="float:right; text-align:right;">#{distance}</div>#{driving_directions}</td></tr><tr><td class="infoWindowBottom">#{locsummary}</td></tr></table></div>'),
    location_summary: new Template('<table width="95%" cellpadding="0" cellspacing="0"><tr><td width="100%">#{name}<br/>#{address}</td></tr><tr><td><table width="100%" cellpadding="0" cellspacing="0"><tr><th align="left" width="50%" nowrap="nowrap">Services Available at Location</th><th align="left" width="50%">Contact Information</th></tr>#{locations}</table></td></tr></table>'),
	overlay_message: new Template('<div class="ovarlay-error">#{message}<br/><br/>#{prompt}<br/><br/><br/>#{actions}</div>'),
	starting_point: new Template('<div class="mapInfoWindow"><table width="100%" cellpadding="0" cellspacing="0"><tr><td class="infoWindowHead" colspan="2">#{title}</td></tr><tr><td class="infoWindowSingle" >#{intro}<br/><br/>#{links}<br/><br/>#{driving_directions}<br/><br/>#{contact}</td></tr></table></div>')
};

var existingMarkers = [];
var overlay_message;
var auto_zoom = false;
var clear_map = false;
var current_center;
var current_bounds;
var refresh_comparison_table = false;

function validateAddressFieldInWizard()
{
	text = $('address_string_wizard').value
	
	if (text == 'Enter Your Address or Zip Code' || text == '') {
		alert("Please enter an address first.");
		return false;
	}
	
	return true;
}

function updateComparisonTable()
{
	clear_map = true;
	refresh_comparison_table = true;
	updateMarkers();
}

function handleKeyPress(e,form, usewizard) {
	var key=e.keyCode || e.which;
	if (key==13){
		
		drugname = $('drug_name') != null ? $('drug_name').value : '';
		
		if (usewizard == 'true')
			drugname = $('drug_name_wizard').value
			
		new Ajax.Request(pharmacy_drug_strength_url, 
			{asynchronous:true, 
			 evalScripts:true,  
			 parameters:'drug_name=' + drugname + '&use_wizard=' + usewizard})
		return false;
	}
	return true;
}

function processWizardKeyPress(e)
{
    if (null == e)
        e = window.event ;
    if (e.keyCode == 13)  {
        document.getElementById("wizard_submit_button").click();
        return false;
    }
}

function drivingDirectionsURL(to_address, lat, lng)
{
   return driving_directions_url + "?address_from=" + $('address_string').value + "&address_to=" + to_address + "&map_center_start=" + lat + "," + lng
}

function initialize(){
	map_dimensions = map.getSize();
	
    // our loading/status/error message
    overlay_message = new OverlayMessage($('map_wrapper'));
    overlay_message.visibleStyle = 'top: -60%; color: #000000; background-image:url(../../images/progress.gif); background-repeat: no-repeat; background-position: 20px center;position: relative; background-color: #FFFFFF; width: 40%; text-align: center; margin-left: auto; margin-right: auto; padding: 20px; z-index: 1000; opacity: .75; filter: alpha(opacity=75);';
    overlay_message.visibleMaximizedStyle = 'top: -' + map_dimensions.height + 'px; color: #ffffff; position: relative; left: 0px; background-color: #000000; height: ' + map_dimensions.height + 'px; width: 100%; text-align: center; font-size:13px; margin-left: auto; margin-right: auto; z-index: 1000; opacity: .75; filter: alpha(opacity=75);';
    
	// generate starting point info window text
	if (!isIndexPage) {
		if (starting_point_details) {
			var sp_info_window = createInfoWindowText($H({
				type: 'single'
			}).merge(starting_point_details)); 
		}
		else {
			var sp_info_window = createInfoWindowText({
				type: 'starting_point',
				address_string: starting_point_address,
				lat: starting_point.getLatLng().lat(),
				lng: starting_point.getLatLng().lng()
			});  
		}
	}
	
	// listen for move start events
    GEvent.addListener(map, "movestart", function(){
        if (!overlay_message.isMaximized()) {
            overlay_message.set("Moving the map");
        }
    });
	
    // listen for zoom events
    GEvent.addListener(map, "zoomend", function(){
        clear_map = true;
		if ($('comp_table_out_of_date'))
		{
			$('comp_table_out_of_date').show();
		}
    });
	
	GEvent.addListener(map, "movestart", function(){
		// capture the current center for snap back
		current_center = map.getCenter();
		current_bounds = map.getBounds();
	});
	
    // listen for pan/move events
    GEvent.addListener(map, "moveend", function(){
		if (!overlay_message.isMaximized()) {
			overlay_message.clear();
		}
		
		if (!isIndexPage && isMapInCounty()) {
				updateMarkers();
				if ($('comp_table_out_of_date'))
				{
					$('comp_table_out_of_date').show();
				}
		} else if (!isMapInCounty()) {
				displayOutsideBoundaryMessage();
		}
    });
	
    // check if the address is invalid or out of bounds
    if (error_could_not_geocode || error_out_of_bounds) {
		var message = templates['overlay_message'].evaluate({
			message: ((error_could_not_geocode) ? ('The address location you entered could not be located.') : ('More information is needed about this address to determine its location.')),
			prompt: 'Please check the location information and try again.<br/><br/>We have centered the map on Atlanta, Georgia.',
			actions: '<a onclick="overlay_message.unlock();overlay_message.clear();return false;" href="#">Close Message</a>'
		});
		overlay_message.setMaximized(message);
		// lock the overlay message until it is dismissed
		overlay_message.lock();
    }
	
	if (isMapInCounty() && !isIndexPage && !error_could_not_geocode && !error_out_of_bounds) {		
		updateMarkers();
	}
}

function updateMarkers(){
    // check if we should clear all markers
    if (clear_map) {
        clear_map = false;
        removeExistingMarkers();
    }
    
    overlay_message.set("Loading location data...");
    
    var bounds = map.getBounds();
    var southWest = bounds.getSouthWest();
    var northEast = bounds.getNorthEast();
    var map_dimensions = map.getSize();
    
    // grab points from the backend via AJAX
    new Ajax.Request(pointsURL, {
        method: 'get',
        parameters: {
            ne: northEast.toUrlValue(),
            sw: southWest.toUrlValue(),
            size: map_dimensions.width + "," + map_dimensions.height,
			type_id: $('type_id') != null ? $('type_id').value : "",
			service_id: $('service_id') != null  ? $('service_id').value : "",
			condition_id: $('condition_id') != null  ? $('condition_id').value : "",
			procedure_id: $('procedure_id') != null  ? $('procedure_id').value : "",
			drug_name: $('drug_name') != null  ? $('drug_name').value : "",
			drug_str: $('drug_str') != null  ? $('drug_str').value : "",
			drug_form: $('drug_form') != null  ? $('drug_form').value : "",
			subtype_id: $('subtype_id') != null  ? $('subtype_id').value : ""},
        onSuccess: function(transport){
			var json = transport.responseText.evalJSON();
            var markers = json.markers;
			var wereAddressesLimited = json.addressesWereLimited;
			
			// Check to see if our query had more than 100 results. If so, we truncated the results. So we should
			// inform the user by turning on a div.
			if (wereAddressesLimited)
			{
				$('address_list_truncated').show();
			}
			else
			{
				$('address_list_truncated').hide();
			}
			
            // check if our query brought  back any markers
            if (markers && markers.length > 0) {
                // we have markers...turn auto_zoom off
                auto_zoom = false;
                // Custom loop with cached length property: maximum full-loop performance on very large arrays!
                for (var index = 0, len = markers.length; index < len; ++index) {
                    var latlng = new GLatLng(markers[index].lat, markers[index].lng);
                    //skip it if the marker already exists
                    //or is not in the viewable area
                    if (!existingMarkers[index] && bounds.containsLatLng(latlng)) {
                        overlay_message.set('Adding point ' + (index + 1) + ' of ' + len);
                        existingMarkers[index] = createMarker(markers[index]);
                        map.addOverlay(existingMarkers[index]);
                        // retrieve marker details on click
						if ((markers[index].type == 'single') ||
                        	(markers[index].type == 'multi_office')) {
                            GEvent.addListener(existingMarkers[index], "click", retrieveLocationDetails(existingMarkers[index], markers[index]));
                        }
                    }
                }
				
				if (refresh_comparison_table) {
					overlay_message.set("Loading comparison table...");
					
					new Ajax.Request(compareURL, {
						asynchronous: true,
						evalScripts: true,
						onLoading:function(request){$('comp_table_loading_div').show()}, 	
						method: 'get',
						parameters: {
							address_string: $('address_string').value,
            				ne: northEast.toUrlValue(),
            				sw: southWest.toUrlValue(),
							type_id: $('type_id') != null ? $('type_id').value : "",
							service_id: $('service_id') != null  ? $('service_id').value : "",
							condition_id: $('condition_id') != null  ? $('condition_id').value : "",
							procedure_id: $('procedure_id') != null  ? $('procedure_id').value : "",
							drug_name: $('drug_name') != null  ? $('drug_name').value : "",
							drug_str: $('drug_str') != null  ? $('drug_str').value : "",
							drug_form: $('drug_form') != null  ? $('drug_form').value : "",
							subtype_id: $('subtype_id') != null  ? $('subtype_id').value : ""}
					});
					
					refresh_comparison_table = false;
				}
                overlay_message.clear();
            // no markers let's zoom out until we find some
            } else {
                // ask the user's permission to zoom out
                if (!auto_zoom) {
					var message = templates['overlay_message'].evaluate({
                		message: 'We were unable to retrieve any location data for your current zoom level.',
                		prompt: 'Would you like the system to zoom out?',
                		actions: '<a onclick="refresh_comparison_table=true;auto_zoom=true;map.setZoom(map.getZoom() - 1);return false;" href="#">Yes</a>&nbsp;&nbsp;<a onclick="overlay_message.clear();return false;" href="#">No</a>'
            		});
					overlay_message.setMaximized(message);
                } else {
					refresh_comparison_table = true;
                    map.setZoom(map.getZoom() - 3);
                }
            }
        },
        onException: function(transport, exception){
            alert(exception)
        }
    })
}

function createMarker(result){
    var point = new GLatLng(result.lat, result.lng);
    var marker;
    //create the marker with the appropriate icon/options
    switch (result.type) {
        case 'cluster':
            marker = new GMarker(point, {
                title: 'Cluster of ' + result.composite_count + ' locations.',
                icon: cluster_icon
            });
            marker.bindInfoWindowHtml(createInfoWindowText(result));
            marker.getIcon().image = (result.composite_count >= 50) ? ("/sfc/images/marker_amenities_cluster_50.png") : ("/sfc/images/marker_amenities_cluster_" + result.composite_count + ".png");
            break;
        case 'multi_office':
            marker = new GMarker(point, {
                title: result.name,
                icon: multi_office_icon
            });
            // set the correct icon
            marker.getIcon().image = (result.composite_count >= 10) ? ("/sfc/images/marker_amenities_combo_10.png") : ("/sfc/images/marker_amenities_combo_" + result.composite_count + ".png");
            break;
        default:
            marker = new GMarker(point, {
                title: result.name,
                icon: single_icon
            });
            break;
    }
    
    return marker;
}

function clusterZoom(point, zoom){
    clear_map = true;
    map.setCenter(new GLatLng(point.lat, point.lng), zoom);
}

function getAdjustedMapBounds(factor){
    var bounds = map.getBounds();
    var sw = bounds.getSouthWest();
    var ne = bounds.getNorthEast();
    width_adjustment = ((ne.lng() - sw.lng()) * factor) / 2;
    height_adjustment = ((ne.lat() - sw.lat()) * factor) / 2;
    return new GLatLngBounds(new GLatLng((sw.lat() - height_adjustment), (sw.lng() - width_adjustment)), new GLatLng((ne.lat() + height_adjustment), (ne.lng() + width_adjustment)));
}

// function returns a loosely joined closure
// see http://vikgup.blogspot.com/2007/03/closures-in-javascript.html
function retrieveLocationDetails(marker, result){
    return function(){
    	overlay_message.set('Loading details for <br/>"'+result.name+'"');
        new Ajax.Request(detailsURL, {
            method: 'get',
            parameters: {
                id: result.id,
				selected_locations: result.selected_ids,
				lat: starting_latlng[0],
				lng: starting_latlng[1]
            },
            onSuccess: function(transport){
				var json = transport.responseText.evalJSON();
                var detail_size = json.details[0].locations.length;
                var type = 'single';
                if (detail_size > 1) {
                    type = 'multi_office';
                }
                // open info window
                marker.openInfoWindowHtml(createInfoWindowText($H({
                    type: type
                }).merge(json.details[0])), {
                    maxWidth: 500
                });
				overlay_message.clear();
            },
            onException: function(transport, exception){
                alert(exception)
            }
        }); // end of the new Ajax.Request() call
    };
}

function createInfoWindowText(result){
	var info_string = '';
    
	switch (result.type) {
        case 'cluster':
			var width = result.width;
			var height = result.height;
			var count = result.composite_count;
			var lat = result.lat;
			var lng = result.lng;
			var bbox = new GLatLngBounds(new GLatLng((lat - (height / 2)), (lng - (width / 2))), new GLatLng((lat + (height / 2)), (lng + (width / 2))));
            var zoom = map.getBoundsZoomLevel(bbox);
			info_string = templates['cluster'].evaluate($H({
                zoom: zoom
            }).merge(result));
            break;
        case 'single':
	        var loc = result.locations.first();
			
			var name_string = loc.name;
			url = getUrl(loc.contacts);
			if (url != null)
				name_string = '<a href="' + url + '" target="_blank">' + loc.name + '</a>';
	        
			var contacts_string = contactsString(loc.contacts);
            var directions_string = templates['driving_directions'].evaluate({
                lat: result.lat,
                lng: result.lng,
				address_from_string: $('address_string').value,
                address_string: result.street + ', ' + result.city + ', ' + result.state + ' ' + result.zip
            });
			var description_string = descriptionString(loc);
			var distance_string = "<strong>Distance: </strong>" + result.distance + "mi";
			
            var address_string = templates['address'].evaluate(result);
            info_string = templates['single'].evaluate({
                title: name_string,
                address: address_string,
                driving_directions: directions_string,
                contact: contacts_string,
                description: description_string,
				distance: distance_string
            });
            break;
        case 'multi_office':
            var name_string = 'Multiple Office Location'; 
            var directions_string = templates['driving_directions'].evaluate({
                lat: result.lat,
                lng: result.lng,
				address_from_string: $('address_string').value,
                address_string: result.street + ', ' + result.city + ', ' + result.state + ' ' + result.zip
            });
			var distance_string = 'Distance: ' + result.distance + "mi";
			var locations_string = '<table>';
                    for (var index = 0, len = result.locations.length; index < len; ++index) {
                        loc = result.locations[index];
						
						var loc_name = loc.name;
						url = getUrl(loc.contacts);
						if (url != null)
							loc_name = '<a href="' + url + '" target="_blank">' + loc.name + '</a>';
								
                        contacts_string = contactsString(loc.contacts);
                        locations_string += '<tr><td valign="top" width="30%">'+
												((loc_name)?(loc_name+'<br/>'):(''))+
												((loc.room)?(loc.room):(''))+
                        						'</td><td valign="top" width="33%">' +
                        						contacts_string + '</td><td valign="top" width="37%">' +
                        						descriptionString(loc) +
                        						'</td></tr>';
                    }
				locations_string +='</table>';	
            var address_string = templates['address'].evaluate(result);
            info_string = templates[result.type].evaluate({
                title: 'Multiple Office Address',
                address: address_string,
                driving_directions: directions_string,
				locsummary: locations_string,
				distance: distance_string
            });
            break;
        default:
            info_string = 'NOT YET IMPLEMENTED';
            break;
    }
    
    return '<div id="info-amenities">' + info_string + '</div>';
}

function drawCountyBoundaries(){
    for (var index = 0, len = boundary_polylines.length; index < len; ++index) {
        boundary = boundary_polylines[index];
        map.addOverlay(boundary);
    }
}

function contactsString(contacts){
	contacts_string = '';
    contacts.each(function(contact){
		if(contact.type.match(/Url/gi)) {
			//contacts_string += '<a href="' + contact.data + '" target="_new">Web Site</a><br/>';
		} else if(contact.type.match(/fax/gi)) {
			contacts_string += 'Fax: ' + contact.data + '<br/>';
		} else {
			contacts_string += 'Phone: ' + contact.data + '<br/>';
		}
    });
	return contacts_string; 
}

function getUrl(contacts)
{
	var url = null;
	contacts.each(function(contact){
		if(contact.type.match(/Url/gi)) {
			url = contact.data;
		}
	});
	
	return url;
}

function descriptionString(loc)
{
	var description_string = ''
	if (loc.entity_type == "HospitalProfile")
	{
		description_string = '<strong>Overall Facility Score: </strong>' + loc.tqi + '<br/><strong>Accreditation: </strong>'
		if (loc.accreditation != 'N/A')
		{
			description_string = description_string + '<a href="http://www.thejointcommission.org" target="_blank">' + loc.accreditation +'</a>';
		}
		else
		{
			description_string = description_string + 'N/A';
		}
	} else if (loc.service && loc.service != '') {
		description_string = '<strong>Other Service Information:</strong> ' + loc.service;
	}
	
	return description_string;
}

function isMapInCounty() {
	var center = map.getCenter();
	var current_zoom = map.getZoom();
	var current_bounds = map.getBounds();
	var county_bounds = boundary_polylines[0].getBounds();
	
	for (var i=0; i<boundary_polylines.length; i++) 
	{
    	var county_bounds = boundary_polylines[i].getBounds();
		
		if ((county_bounds.containsBounds(current_bounds) || 
			county_bounds.intersects(current_bounds)) && (current_zoom > 1)) {
			return true;
		}	
	}
	
	return false;
}

function removeExistingMarkers(bounding_box) {
	//loop through each of the points in memory and remove those that
    //aren't going to be shown
    for (var index = 0, len = existingMarkers.length; index < len; ++index) {
		map.removeOverlay(existingMarkers[index]);
        delete existingMarkers[index];
    }
	
	// remove null values from marker array
	existingMarkers = existingMarkers.compact();
}

function displayOutsideBoundaryMessage() {
	var message = 'You have panned outside of the state boundary.';
	var actions = '<a onclick="overlay_message.clear();map.panTo(current_center);return false;" href="#">Close Message</a>'
	
	// build zoomed out of bounds error message
	if (map.getZoom() <= 1) {
		message = 'You have zoomed outside of the state boundary.';
		actions = '<a onclick="overlay_message.clear();map.setCenter(current_center,8);return false;" href="#">Close Message</a>';
	}
				
	var message = templates['overlay_message'].evaluate({
		message: message,
		prompt: 'You will now be moved back.',
		actions: actions
	});
	
	overlay_message.setMaximized(message);
}
