
//= depends: script_src_request

ZAPI.namespace('GoogleMaps').ClientGeocoder = Class.create(google.maps.ClientGeocoder, (function() {
	var googleReqId = 0;

	return {
		initialize: function() {
			google.maps.ClientGeocoder.apply(this, arguments);
			this.setBaseCountryCode('CA');
		},

		getLatLng: function(address, callback, options) {
			callback = callback.wrap(function(proceed, json) {
				if (json.Status.code === google.maps.GEO_SUCCESS) {
					proceed(new google.maps.LatLng(
						json.Placemark[0].Point.coordinates[1],
						json.Placemark[0].Point.coordinates[0]
					));
				}
				else {
					proceed(null);
				}
			});
			this.getLocations(address, callback, options);
		},

		getLocations: function(loc, callback, options) {
			options = $H({
				'zapiQueryParams': undefined,
				'googleQueryParams': undefined,
				'useZAPI': true,
				'useGoogle': true
			}).merge(options);

			if (options.get('useZAPI')) {
				this.getLocationsFromZAPI(loc, callback, options);
			}
			else {
				this.getLocationsFromGoogle(loc, callback, options);
			}
		},

		getLocationsFromZAPI: function(loc, callback, options) {
			if (this.getCache() && this.getCache().get(loc)) {
				callback(this.getCache().get(loc));
				return;
			}

			options = $H({}).merge(options);

			var requestUrl = '/zapi/geospatial_lookup/',
				queryParams = ZAPI.DEFAULT_API_PARAMS.merge(options.get('zapiQueryParams')),
				responseType;

			if (loc instanceof google.maps.LatLng) {
				requestUrl += 'reverse_geocode';
				queryParams.set('query', loc.toUrlValue());
				responseType = 'ReverseGeocode';
			}
			else {
				requestUrl += 'geocode';
				queryParams.set('query', loc);
				responseType = 'Geocode';
			}

			(new Ajax.Request(requestUrl, $H({
				'method': 'get',
				'parameters': queryParams.toQueryString(),
				'onSuccess': function(response) {
					var json = response.responseJSON,
						content = json.ZAPI.GeospatialLookup[responseType];
					if (content.Status.code === google.maps.GEO_SUCCESS) {
						if (this.getCache()) {
							this.getCache().put(loc, content);
						}
						callback(content);
					}
					else if (options.get('useGoogle')) {
						this.getLocationsFromGoogle(loc, callback, options);
					}
					else {
						callback(content);
					}
				}.bind(this),
				'onFailure': function() {
					if (options.get('useGoogle')) {
						this.getLocationsFromGoogle(loc, callback, options);
					}
					else {
						callback(null);
					}
				}.bind(this)
			}).merge(options.get('zapiRequestOptions')).toObject()));
		},

		getLocationsFromGoogle: function(loc, callback, options) {
			options = $H({
				'googleRequestOptions': {
					'callbackParamName': 'callback',
					'callbackName': '_google_ssr_' + (googleReqId++) + '_'
				}
			}).merge(options);

			if (options.get('googleQueryParams')) {
				var requestUrl,
					queryParams = $H({
						'output': 'json',
						'oe': 'utf-8',
						'mapclient': 'jsapi',
						'hl': 'en'
					}).merge(options.get('googleQueryParams'));

				if (!queryParams.get('key')) {
					throw new Error("You must supply a valid Google Maps API key");
				}

				requestUrl = 'http://maps.google.com/maps/geo?' + queryParams.toQueryString();

				(new ZAPI.ScriptSrcRequest(requestUrl, $H({
					'onSuccess': function(json) {
						callback(json);
					},
					'onFailure': function() {
						callback(null);
					}
				}).merge(options.get('googleRequestOptions'))));
			}
			else {
				ZAPI.GoogleMaps.ClientGeocoder.superclass.prototype.getLocations.apply(this, [ loc, callback ]);
			}
		},

		getAddressInBounds: function(loc, callback, options) {
			options = $H({
				'zapiQueryParams': undefined,
				'googleQueryParams': undefined,
				'useZAPI': true,
				'useGoogle': true
			}).merge(options);

			if (options.get('useZAPI')) {
				this.getLocationsFromZAPI(loc.getCenter(), callback, options);
			}
			else {
				ZAPI.GoogleMaps.ClientGeocoder.superclass.prototype.getAddressInBounds.apply(this, [ loc, callback ]);
			}
		},

		fetchLocation: function(ids, callback, options) {
			var requestUrl = '/zapi/geospatial_lookup/fetch',
				queryParams,
				loc;

			ids = $A([ ids ]).flatten().join(','),
			loc = 'Geospatial ' + ids;
			options = options || {};

			if (this.getCache() && this.getCache().get(loc)) {
				callback(this.getCache().get(loc));
				return;
			}

			queryParams = ZAPI.DEFAULT_API_PARAMS.merge({
				'id': ids
			}).merge(options);

			(new Ajax.Request(requestUrl, {
				'method': 'get',
				'parameters': queryParams.toQueryString(),
				'onSuccess': function(response) {
					var content = Object.traverse(response, $w('responseJSON ZAPI GeospatialLookup Fetch'));
					if (content && content.Status.code === google.maps.GEO_SUCCESS) {
						if (this.getCache()) {
							this.getCache().put(loc, content);
						}
					}
					callback(content);
				}.bind(this),
				'onFailure': function() {
					callback(null);
				}.bind(this)
			}));
		}
	};
}()));
