/**
* @name HiveGeolocation
* @version 0.9.0
* @author Joe Johnston <joe@socialhive.org>
* @copyright (c) 2009 Joe Johnston
* http://socialhive.org/hivemaps
* http://code.google.com/p/hivemaps
*
* Creates SOCIALHIVE.geo.location
*
* Currently supports geolocation detection through Google's AJAX ClientLocation object, Google Gears, and
* the browsers built-in navigator.geolocation objection if available.  Future versions to support 
* Loki's API and possibly other 3rd party IP address, GPS, and wifi detection.
*
* Usage:
*	// Try to get location unobtrusively through JavaScript and then fail back to browser based detection
* 	SOCIALHIVE.geo.location.getLocation(
*		function(position){alert('Success!');},  // success callback
*		function(err){alert('Error');}					 // error callback
*	);
*
*  // Or directly call a specific getCurrentPosition method
*  SOCIALHIVE.geo.location.gears.getCurrentLocation(successCallback, errorCallback, options);
*  SOCIALHIVE.geo.location.ajax.getCurrentLocation(successCallback, errorCallback, options);
*  navigator.geolocation.getCurrentLocation(successCallback, errorCallback, options);
*  
*  Note that navigator.gelocation is automatically set to either gears or ajax if not 
*	defined by the browser.
*  
* Thanks to Google for the sample code ...
* http://google-ajax-examples.googlecode.com/svn/trunk/whereareyou/scripts/geometa.js
**/

/**
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/

jQuery(function($){
  var GearsGeoLocation = (function(){
    var geo = (google.gears) ? google.gears.factory.create('beta.geolocation') : null;
    return {
      shim: true,
      type: "Gears",
      lastPosition: null,
      getCurrentPosition: function(successCallback, errorCallback, options) {
        var self = this;
        var sc = function(position){
          successCallback(position);
          self.lastPosition = position;
        };
        var ec = function(error){
          AjaxGeoLocation.getCurrentPosition(sc, errorCallback, options);
        };
        geo.getCurrentPosition(sc, ec, options);
      },
      watchPosition: function(successCallback, errorCallback, options) {
        geo.watchPosition(successCallback, errorCallback, options);
      },
      clearWatch: function(watchId) {
        geo.clearWatch(watchId);
      },
      getPermission: function(siteName, imageUrl, extraMessage) {
        geo.getPermission(siteName, imageUrl, extraMessage);
      }
    };
  })();

  var AjaxGeoLocation = (function(){
    var loading = false;
    var loadGoogleLoader = function() {
      if (!hasGoogleLoader() && !loading) {
        loading = true;
        var s = document.createElement('script');
        s.src = 'http://www.google.com/jsapi?callback=_google_loader_apiLoaded';
        s.type = "text/javascript";
        document.getElementsByTagName('body')[0].appendChild(s);
      }
    };
    var queue = [];
    var addLocationQueue = function(callback) {
      queue.push(callback);
    }
    var runLocationQueue = function() {
      if (hasGoogleLoader()) {
        while (queue.length > 0) {
          var call = queue.pop();
          call();
        }
      }
    }
    window['_google_loader_apiLoaded'] = function() {
      runLocationQueue();
    }
    var hasGoogleLoader = function() {
      return (window['google'] && google['loader']);
    }
    var checkGoogleLoader = function(callback) {
      if (hasGoogleLoader()) return true;
      addLocationQueue(callback);
      loadGoogleLoader();
      return false;
    };
    loadGoogleLoader(); // start to load as soon as possible just in case

    return {
      shim: true,
      type: "ClientLocation",
      lastPosition: null,
      getCurrentPosition: function(successCallback, errorCallback, options) {
        var self = this;
        if (!checkGoogleLoader(function() {
          self.getCurrentPosition(successCallback, errorCallback, options);
        })) return;
        if (google.loader.ClientLocation) {
          var cl = google.loader.ClientLocation;
          var position = {
            latitude: cl.latitude,
            longitude: cl.longitude,
            altitude: null,
            accuracy: 43000, // same as Gears accuracy over wifi?
            altitudeAccuracy: null,
            heading: null,
            velocity: null,
            timestamp: new Date(),

            // extra info that is outside of the bounds of the core API
            address: {
              city: cl.address.city,
              country: cl.address.country,
              country_code: cl.address.country_code,
              region: cl.address.region
            }
          };
          successCallback(position);
          this.lastPosition = position;
        } else if (typeof errorCallback === "function")  {
          errorCallback({ code: 3, message: "Using the Google ClientLocation API and it is not able to calculate a location."});
        }
      },
      watchPosition: function(successCallback, errorCallback, options) {
        this.getCurrentPosition(successCallback, errorCallback, options);
        var self = this;
        var watchId = setInterval(function() {
          self.getCurrentPosition(successCallback, errorCallback, options);
        }, 10000);

        return watchId;
      },
      clearWatch: function(watchId) {
        clearInterval(watchId);
      },
      getPermission: function(siteName, imageUrl, extraMessage) {
        return true;
      }
    };
  })();

	function getLocation(successCallback, errorCallback, options) {
		// try unobtrusive AJAX detection first and fallback to navigator.geolocation
		var sc = function(position){
			if (position.coords) {
				position = position.coords;
			}
			if (!position.latitude || !position.longitude || !position.accuracy) {
				navigator.geolocation.getCurrentPosition(successCallback, errorCallback, options);				
			} else {
				successCallback(position);
			}
		};
		var ec = function(error){
			navigator.geolocation.getCurrentPosition(sc, errorCallback, options);
		};
		AjaxGeoLocation.getCurrentPosition(sc, ec, options);
	}

	var HiveGeolocation = {
		gears: GearsGeoLocation,
		ajax: AjaxGeoLocation,
		getLocation: getLocation		
	};
	
	if (! window.SOCIALHIVE)
		window.SOCIALHIVE = {};
	if (!window.SOCIALHIVE.geo)
    window.SOCIALHIVE.geo = {};
	window.SOCIALHIVE.geo.location = HiveGeolocation;
		
	if (typeof navigator.geolocation == "undefined" || navigator.geolocation.shim)
  	navigator.geolocation = (window.google && google.gears && google.gears.factory.create) ? GearsGeoLocation : AjaxGeoLocation;
});
