(function($) {
  var gas = new base2.Package(this, {
    name:    "gas",
    version: "1.0",
    parent:  dashfly,
    imports: "dashfly",
    exports: "FuelGrade,GasGateway,GasBuddy"
  });
  
  eval(this.imports); 
  
  var FuelGrade = {
	  Regular: 'A',
	  Midgrade: 'B',
	  Premium: 'C',
	  Diesel: 'D'
  };
	  
  var GasGateway = base2.Abstract.extend({
	  getBestStations: function() {
	  	throw "getBestStations not implemented!";
  	  }
  });
   
  var GasBuddy = GasGateway.extend({
	  constructor: function(options) {
	  	$.extend(this, {
			async: true,
			useClient: true,
			clientUrl: 'http://xml.gasbuddy.com/Get_DashFly_Prices.aspx?lat=%1&long=%2&f=%3&c=dashfly&k=pb58sgt6jkd&cb=%4',
			serverUrl: '/ajaxcall/gasBuddy.php?lat=%1&lng=%2&fuel=%3',
			timeout: 20000
		}, options || {});
  	  },

  	  getBestStations: function(lat, lng, fuel, block, error, context) {
  		var uid, url, dataType;
	  		
		if (this.useClient) {
			dataType = 'script';
			uid = registerCallback(function(t, response) {
				if ($.isFunction(block)) {
					block.call(context, this.canonicalize(response), this);
				}
			}, this);
			url = format(this.clientUrl, lat, lng, fuel, getCallbackQuery(uid));
		} else {
			dataType = 'text';
			url = format(this.serverUrl, lat, lng, fuel);		
		}
	  		
  		$.ajax({
  			type: 'GET',
  			dataType: dataType,
  			url: url, 
  			timeout: this.timeout,
  			global: false,
  			async: this.async,
  			success: delegate(function(options,result,status) {
  				if (!this.useClient && $.isFunction(block)) {
  					block.call(context, this.canonicalize(result), this);
  				}	
  			}, this),
  			error: delegate(function(options,xhr,status,err) { 
  				if ($.isFunction(error)) {
  					error.call(context, xhr.statusText, err, this);
  				}
  			}, this),
  			complete: function(options,xhr,status) {
  				if (uid) { unregisterCallback(uid); }
  			}
  		});	
	  },
	  
	  canonicalize: function(response) {
		var xml = parseXml(response);
		return $.map($(xml).find('/GBPrice/Price'), function(item) {
			var tempPrice = $(item).find('price').text();
			return { price: tempPrice.substring(0, tempPrice.length - 1),
					 name: $(item).find('station_nm').text(),
					 fuel: $(item).find('fuel_type').text(),
					 address:$.trim($(item).find('address').text()),
					 city: $.trim($(item).find('city').text()),
					 state: $(item).find('state').text(),
					 lat: parseFloat($(item).find('lat').text()),
					 lng: parseFloat($(item).find('long').text()),
					 distance: parseFloat($(item).find('distance').text()),
					 time: new Date(Date2.parse($(item).find('tme').text()))
					 };
		});			  
	  }  
  	}
  );
  	
  var GasModule = Module.extend({
	  load: function() {
		  $.extend(this, {
			  fuel: FuelGrade.Regular,
			  timeLimit: 48,
			  mapManager: this.getService(map.MapManager),
			  routeManager: this.getService(route.RouteManager)
		  });
			
		  this.locations = new Array2();
		  this.gasGateway = new GasBuddy();
			
		  if (!instanceOf(this.gasGateway, GasGateway)) {
			  throw "The gasGateway option does not implement GasGateway";
		  }
		  
		  this.createStationInfoTemplate();
		  this.toolbar = new GasToolbar(this, { 
			  visible: false, urls: this.application.getUrls()
			  });
		  this.baseIcon = this.createBaseIcon();
		  this.setVisible(false);
		  this.trackLocations();		  
	  },
	  
	  getMap: function() {
		  return this.mapManager.getMap();
	  },
	  
	  getFuelGrade: function() {
		  return this.fuel;
	  },

	  setFuelGrade: function(fuel) {
		  if (this.fuel != fuel) {
			  this.fuel = fuel;
			  this.refreshLocations(this.locations.slice());
			  $(this).triggerHandler('changed', 'fuel');
		  }
	  },
		
	  getTimeLimitHours: function() {
		  return this.timeLimit;
	  },

	  setTimeLimitHours: function(timeLimit) {
		  if (this.timeLimit != timeLimit) {
			  this.timeLimit = timeLimit;
			  this.configureStationMakers(this.locations.pluck('gasStations'));
			  $(this).triggerHandler('changed', 'timeLimitHours');
		  }
	  },
	
	  isVisible: function() {
		  return this.visible;
	  },
	  
	  setVisible: function(visible) {
		  if (this.visible != visible) {
			  this.visible = visible;
			  if (this.visible) {
				  this.showLocations();
			  }
			  else {
				  this.hideLocations();
			  }
			  $(this).triggerHandler('changed', 'visibility');
		  }
	  },
	  
	  addLocations: function(locations) {
		  Array2.forEach(Array2.flatten([locations]), function(location) {
			  if (!this.locations.contains(location)) {
				  this.locations.push(location);
			  }		  
			  this.showLocations(location);
		  }, this);
		  this.toolbar.setVisible(true);
		  return this;
	  },
		
	  removeLocations: function(locations) {
		  locations = locations || this.locations;
		  Array2.forEach(Array2.flatten([locations]), function(location) {
			  if (this.locations.contains(location)) {
				  if (location.gasStations) {
					  Array2.forEach(location.gasStations, function(station) {
						  if (station.marker) {
							  station.marker.closeInfoWindow();
							  this.getMap().removeOverlay(station.marker);
							  delete station.marker;
						  }
					  }, this);
				  }
				  delete location.gasStations;
				  this.locations.remove(location);
			  }		  
		  }, this);
		  if (this.locations.length === 0) {
			  this.toolbar.setVisible(false);
		  }
		  return this;
	  },
		
	  refreshLocations: function(locations) {
		  this.removeLocations(locations);
		  this.addLocations(locations);
		  return this;
	  },
	
	  showLocations: function(locations) {
		  if (!this.visible) { return this; }
		  locations = locations || this.locations;
		  Array2.forEach(Array2.flatten([locations]), function(location) {
			  if (!location.gasStations) {
				  var latLng = location.GetLatLng();
				  this.gasGateway.getBestStations(latLng.lat(), latLng.lng(), this.fuel,
					  delegate(function(t, gasStations) {
						 this.assignGasStations(location, gasStations);
						 this.showLocationOnMap(location);
					  }, this)
				  );			  
			  } else {
				  this.showLocationOnMap(location);
			  }
		  }, this);		  
		  return this;
	  },
	  
	  showLocationOnMap: function(location) {
		  if (location.gasStations) {
			  Array2.forEach(location.gasStations, function(station) {
				  if (station.marker) {
					  if (!station.markerAdded) {
						  this.getMap().addOverlay(station.marker);
						  station.markerAdded = true;
					  }
					  station.marker.show();
				  }
			  }, this);
			  
			  if (this.locations.length <= 3) {
				  var points = this.locations.map(function(point) {
					  var p = [point.GetLatLng()];
					  if (point.gasStations) {
						  p = p.concat(point.gasStations.map(function(station) {
							  return new GLatLng(station.lat, station.lng);
						  }));
					  }
					  return p;
				  });
				  this.mapManager.centerPointsInMap(points.flatten());
			  }
		  }
	  },
	  
	  hideLocations: function(locations) {
		  locations = locations || this.locations;	  
		  Array2.forEach(Array2.flatten([locations]), function(location) {	    
			  if (location.gasStations) {
				  Array2.forEach(location.gasStations, function(station) {
					  if (station.marker) {
						  station.marker.closeInfoWindow();
						  station.marker.hide();
					  } 
				  });
			  }
		  }, this);
		  return this;
	  },
	  
	  trackLocations : function() {
		  var _this = this;
		  GEvent.addListener(this.getMap(), "addoverlay", function(marker) {
			 var location = _this.findLocationFromMarker(marker);
			 if (location) { _this.addLocations(location); }
		  });
		  GEvent.addListener(this.getMap(), "removeoverlay", function(marker) {
			 var location = _this.findLocationFromMarker(marker);
			 if (location) { _this.removeLocations(location); }
		  });
		  GEvent.addListener(this.getMap(), "clearoverlays", function(marker) {
			 _this.removeLocations(); 
		  });		  
	  },
	  
	  findLocationFromMarker: function(marker) {
		  var location;
		  if (addList) { Array2.some(addList, function(address) {
			  if (address.GetMarker() && instanceOf(marker, GMarker) && 
				  (address.GetMarker().getIcon().image  == marker.getIcon().image)) {
				  location = address;
				  return true;
			  }
			  return false;
		  });}
		  return location;
	  },
	  
	  assignGasStations: function(location, gasStations) {
		  var _this = this;
		  var now = new Date();
		  Array2.forEach(gasStations, function(station) {
			  if (!station.marker) {
				  var latlng = new GLatLng(station.lat, station.lng);
				  var old = this.isStaleTimestamp(station.time);
				  var image = this.getPriceImage(station.price, false, old);				  
				  var options = { 
						  icon: new GIcon(this.baseIcon, image),
						  title: station.name + ' - $' + station.price,
						  zIndexProcess: function() { return -900000000; }
						  };
				  station.marker = new GMarker(latlng, options);
				  GEvent.addListener(station.marker, "mouseover", function() {
					  _this.configureStationMakers(station, true);
				  });	
				  GEvent.addListener(station.marker, "mouseout", function() {
					  _this.configureStationMakers(station, false);
				  });
				  GEvent.addListener(station.marker, "click", function() {
					  with(_this.stationInfo) {
						  find("span[@name='name']").html(station.name);
						  find("span[@name='price']").html("$" + station.price);
						  find("span[@name='address']").html(station.address);
						  find("span[@name='fuel']").html(station.fuel);
						  find("span[@name='cityState']").html(station.city + ",  " + station.state);
						  find("span[@name='distance']").html(station.distance.toString());
						  find("img:eq(1)").attr({ 
							  src: location.GetMarker().getIcon().image,
							  title: location.GetActualAddress()
						  	  });
						  find("a:first").visible(_this.routeManager.canAddAddress());						  
						  data('location', location);
						  data('station', station);
					  }

					  var panoClient = new GStreetviewClient();
					  panoClient.getNearestPanorama(latlng, function(panoData) {
						  if (panoData && panoData.code == 200) {
							  if (_this.panoView) {
								  _this.panoView.remove();
								  delete _this.panoView;
							  }
							  var panoDiv = _this.stationInfo.find("div[@name='panoDiv']");
							  _this.panoView = new GStreetviewPanorama(panoDiv[0], {latlng: panoData.location.latlng});
							  GEvent.addListener(_this.panoView, "error", function(errorCode) {
								  if (errorCode == 603) {
									  panDiv.html('<br/><br/><br/>Flash is not supported by your browser');
								  }
								  else if (errorCode == 600) {
									  panDiv.html('<br/><br/><br/>Street view is not available for this station');
								  }
							  });
						  }
					  });
					  
					  station.marker.openInfoWindow(_this.stationInfo[0]);
				  });
			  }
		  }, this);
		  location.gasStations = gasStations;
	  },
	  
	  configureStationMakers: function(gasStations, selected) {
		  Array2.forEach(Array2.flatten([gasStations]), function(station) {
			  if (station.marker) {
				  var old = this.isStaleTimestamp(station.time);
				  var image = this.getPriceImage(station.price, selected, old);
				  station.marker.setImage(image);
			  } 
		  }, this);	  
	  },
	  
	  isStaleTimestamp: function(timestamp) {
		  if (this.timeLimit <= 0) { return false; }
		  var hours = Math.ceil((new Date().getTime() - timestamp.getTime()) / ONE_HOUR);
		  return hours > this.timeLimit;
	  },
	  
	  createStationInfoTemplate: function() {
		  this.stationInfo = $(document.createElement('div')).createAppend([
		     'div', { style: 'width: 400px; height: 236px' }, [
		        'div', {}, [
		            'span', { name: 'name', style: 'margin-right:4px; font-size: 16px; font-weight: bold' }, '',                                                                                     
		            'img', { src: this.getUrls().forImage('zoom.png'), style: 'cursor: pointer', title: 'Zoom in' }, '',
		            'a', { href: 'javascript:void(0)', style: 'margin-left:4px; font-size: 14px' }, 'Add to route'
		        ],
		        'div', { style: 'font-size: 11px' }, [
		            'span', { name: 'address' }, '', 'span', {}, ', ',
		            'span', { name: 'cityState' }, ''                            
		            ]           
		        ],
		     'img', { style: 'position: absolute; top: 0px; right: 12px; cursor: pointer' }, '',		        
		     'div', { style: 'position: absolute; top:-8px; right: 38px; font-size: 11px' }, [
		        'div', {}, [                                                   
		            'span', { name: 'price', style: 'color: rgb(51, 102, 153); font-size: 22px; font-weight: bold' }, '',
                    'span', { name: 'fuel', style: 'margin-left: 4px; font-size: 14px;font-weight: bold' }, ''
  		            ],
 		        'div', { align: 'right', style: 'font-size: 11px' }, [
 		            'span', { name: 'distance' }, '',
 		            'span', {}, ' miles from'
 		            ]        
 		         ],
			 'div', { name: 'panoDiv', style: 'position: absolute; top: 38px; width: 402px; height: 200px; font-size: 18px', align: 'center' }, ''
			 ]
		  ).css({'margin-top': '-8px', 'position': 'relative', 'z-index': '10'});
		  	  		  
		  $('img:first', this.stationInfo).click(delegate(function() {
			  var station = this.stationInfo.data('station');
			  var latlng = new GLatLng(station.lat, station.lng);
			  station.marker.closeInfoWindow();
			  this.getMap().setCenter(latlng, Math.min(19, this.getMap().getZoom() + 2));
		  }, this));
		    
		  $('img:eq(1)', this.stationInfo).click(delegate(function() {
			  var station = this.stationInfo.data('station');
			  var location = this.stationInfo.data('location');
			  station.marker.closeInfoWindow();
			  this.getMap().setCenter(location.GetLatLng());
		  }, this));
		  
		  $("a:first", this.stationInfo).click(delegate(function() {
			  var station = this.stationInfo.data('station');
			  station.marker.closeInfoWindow();
			  var address = new DashFlyAddress();
			  address.SetLatLng(new GLatLng(station.lat, station.lng));
			  address.SetNotes(['Gas Station: $' + station.price + ' ' + station.fuel]);
			  this.mapManager.reverseGeocode(address, function() {
				  var label = station.name +':'+address.GetAddress();
				  address.SetAddressLabel(label);
				  this.routeManager.addAddress(address);
			  }, this);
		  }, this));
	  },
	  
	  getPriceImage: function(price, selected, old) {
		  var image = old ? 'old' : price.toString();
		  return this.getUrls().forImage('us19/pump' + (selected ? '_o' : '') + image + '.gif');
	  },
	  
	  createBaseIcon: function() {
		  var baseIcon = new GIcon();
		  baseIcon.iconSize = new GSize(25, 15);
		  baseIcon.iconAnchor = new GPoint(12, 15);
		  baseIcon.infoWindowAnchor = new GPoint(12, 2);
		  baseIcon.transparent = "http://www.google.com/intl/en_ALL/mapfiles/markerTransparent.png";
		  return baseIcon;
	  }
  });
  
  var GasToolbar = base2.Base.extend({
	  constructor: function(model, options) {	  
	  	$.extend(this, {
		    model: model,
		    position: new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(100, 7)),
		    timedInfoFormat: '%1 Prices in the Past %2 Hours shown.',
		    allInfoFormat: '%1 Prices and All Stations shown.'				  
		}, options || {});
	  
	  	$(this.model).bind('changed', delegate(this.modelChanged, this));
	  	this.createMapControl(options);
	  },
	  
	  modelChanged: function(source, property, type) {
		  var visible = this.model.isVisible();
		  var timeLimit = this.model.getTimeLimitHours();
		  var fuelOpt =  $('#ddlFuelType option[@value="'+this.model.getFuelGrade()+'"]');
		  var timeOpt =  $('#ddlTimeLimit option[@value="'+timeLimit+'"]');
		  fuelOpt.attr('selected', true); timeOpt.attr('selected', true);
		  var infoFormat = (timeLimit > 0) ? this.timedInfoFormat : this.allInfoFormat;
		  var info = visible ? format(infoFormat,fuelOpt.text(), timeLimit) : '';
		  var action = visible ? 'hide' : 'Show gas stations and prices';
		  var tooltip = visible ? 'Remove gas stations and prices' : '';
		  $('#toggleGas').html(action).attr('title', tooltip);
		  $('#gasInfo').html(info);		  
	  },
	  
	  createMapControl : function(options) {
		  var html = '' +
		  '<div style="border: 1px solid black; background-color: white; cursor: pointer; right: 10.2em; opacity: 0.85;" > ' +
			  '<div style="border-style: solid; border-color: white rgb(176, 176, 176) rgb(176, 176, 176) white; border-width: 1px; font-size: 12px;"> ' +
			  '<img src="' + this.urls.forImage('gas.ico') + '" style="float:left" title="Toggle gas options on/off"> ' +
			  '<span id="gasInfo" style="font-weight: bold;margin-left: 4px"></span> ' +
			  '<a id="toggleGas" class="link" style="margin-right: 4px"></a></div> ' +
			  '<div id="gasOptions" style="display: none; cursor: default; z-index: 1; opacity: 0.78;"> ' +
			  '<table cellspacing="0" cellpadding="0"> ' +
			  '<tbody> ' +
			  '<tr> ' +
				  '<td align="left" style="font-size: 11px; line-height: 15px;"> ' +
				  	'<span style="margin-left:21px; font-weight: bold; color: rgb(0, 51, 102)">Fuel:</span> ' +
				  '</td> ' +
				  '<td> ' +
				  	'<select id="ddlFuelType" style="margin-left:4px;font-size: 11px"> ' +
				  		'<option value="' + FuelGrade.Regular + '">Regular</option> ' +
				  		'<option value="' + FuelGrade.Midgrade + '">Midgrade</option> ' +
				  		'<option value="' + FuelGrade.Premium + '">Premium</option> ' +
				  		'<option value="' + FuelGrade.Diesel + '">Diesel</option> ' +
				    '</select> ' +
				  '</td> ' +
				  '<td align="right" style="font-size: 11px; line-height: 15px;"> ' +
				  	'<span style="margin-left:16px;font-weight: bold; color: rgb(0, 51, 102)">Time:</span> ' +
				  '</td> ' +
				  '<td> ' +
				    '<select id="ddlTimeLimit" style="margin-left:4px;font-size: 11px"> ' +
				    	'<option value="4">4 Hours</option> ' +
				    	'<option value="8">8 Hours</option> ' +
				    	'<option value="12">12 Hours</option> ' +
				    	'<option value="24">24 Hours</option> ' +
				    	'<option value="48">48 Hours</option> ' +
				    	'<option value="72">72 Hours</option> ' +
				    	'<option value="96">96 Hours</option> ' +
				    	'<option value="0">All Stations</option> ' +
				    '</select> ' +
				  '</td> ' +
			  '</tr> ' +
			  '</tbody> ' +
			  '</table> ' +
			  '</div> ' +
		  '</div>';
	
		  this.extend(new GHtmlControl(html, options));		  
		  this.model.getMap().addControl(this, this.position);
		  
		  $('img:first', this.div).click(delegate(function() {
			  if (this.model.isVisible()) { $('#gasOptions').toggle(); }
		  }, this));		  
		  $('#ddlFuelType').change(delegate(function(fuel) {
			  this.model.setFuelGrade($(fuel).val());
			  }, this));
		  $('#ddlTimeLimit').change(delegate(function(timeLimit) {
			  this.model.setTimeLimitHours($(timeLimit).val());
		  }, this));
		  $('#toggleGas').click(delegate(function(input) {
			  var visible = this.model.isVisible();
			  $('#gasOptions').visible(!visible);
			  this.model.setVisible(!visible);
		  }, this));		  
	  }
  });
  
  eval(this.exports);
})(jQuery);
