(function() {
    // If already defined no need to redefine
    if (jQuery.fn.nGeoCoder) {
        return;
    }

    /**
     * jQuery.fn.nGeoCoder enable geocoder on an input box
     */
    jQuery.fn.nGeoCoder = function() {
        return this.each(function() {
            var input = jQuery(this);

            function locationChanged() {
                var geocoder = new google.maps.ClientGeocoder();
                geocoder.getLatLng(input.val(), function(point) {
                    if (point) {
                        input.trigger('locationChanged', point);
                    }
                });
            }

            input.keyup(function(e) {
                if (e.keyCode == 13) {
                    locationChanged();
                }
            });

            input.bind('findLocation', locationChanged);
        });
    };
})();

(function() {
    // If already defined no need to redefine
    if (jQuery.nGeoUtil) {
        return;
    }

    /**
     * jQuery.nGeoUtil, utilities to work with geo location
     */
    jQuery.nGeoUtil = {};

    /**
     * jQuery.nGeoUtil.google, utilities to work with Google maps APIs
     */
    jQuery.nGeoUtil.google = {
        /**
         * Get location as reported by Google loader
         * 
         * @return location or Salzburg if not available
         */
        loaderLocation : function() {
            if (google && google.loader && google.loader.ClientLocation) {
                var lat = google.loader.ClientLocation.latitude;
                var lng = google.loader.ClientLocation.longitude;

                jQuery('#hoverLatitude').text(lat);
                jQuery('#hoverLongitude').text(lng);

                return new google.maps.LatLng(lat, lng);
            }
            return new google.maps.LatLng(47.8225, 13.0415); // Salzburg
        },

        /**
         * Center map in client's current location. This function tries to
         * discover the client's location by first consulting HTML5 GeoLocation
         * if available, if not the function tries to use Google gears to
         * determine the client's location.
         * 
         * @param map
         *            the map to center
         */
        centerInCurrentLocation : function(map) {
            var centered = false;
            if (navigator.geolocation) {
                navigator.geolocation
                .getCurrentPosition(function(position) {
                    var currentLocation = new google.maps.LatLng(
                        position.coords.latitude,
                        position.coords.longitude);
                    map.setZoom(14);
                    map.panTo(currentLocation);
                    centered = true;
                });
            } else {
                if (google && google.gears && google.gears.factory
                    && google.gears.factory.create) {
                    var geo = google.gears.factory.create('beta.geolocation');
                    if (geo) {
                        centered = true;
                        function updatePosition(position) {
                            var currentLocation = new google.maps.LatLng(
                                position.latitude, position.longitude);
                            map.setZoom(14);
                            map.panTo(currentLocation);
                        }
                        function handleError(positionError) {
                        }

                        geo.getCurrentPosition(updatePosition, handleError);
                    }
                }
            }

            if (!centered) {
                var currentLocation = new google.maps.LatLng(47.8225, 13.0415);
                map.setZoom(14);
                map.panTo(currentLocation);
            }
        },

        /**
         * Create a map, replacing the container's content. By default the
         * function tries to locate the client using
         * jQuery.nGeoUtil.google.loaderLocation() function, sets zoom level to
         * 14, and map type to google.maps.MapTypeId.ROADMAP
         * 
         * @param containerId
         *            where to position the map
         * @param options
         *            Google map options
         * @return new map
         */
        createMap : function(containerId, options) {
            var latlng = jQuery.nGeoUtil.google.loaderLocation();

            var settings = jQuery.extend( {
                zoom : 14,
                center : latlng,
                draggableCursor : 'crosshair'
            }, options);

            return new google.maps.Map(document.getElementById(containerId),
                settings);
        },

        /**
         * Enable or disable, drag capability of marker
         * 
         * @param marker
         *            the marker to change drag capability of
         * @param draggable
         *            true if marker should be draggable, false otherwise
         */
        setDraggable : function(marker, draggable) {
            marker.set('draggable', draggable);
        },

        /**
         * Show marker
         * 
         * @param marker
         *            the marker to show
         */
        show : function(marker) {
            marker.show();
        },

        /**
         * Hide marker
         * 
         * @param marker
         *            the marker to hide
         */
        hide : function(marker) {
            marker.hide();
        },

        /**
         * Adds a 'bounds_changed' listener to Google map
         * 
         * @param map
         *            the map to listen to
         * @param listener
         *            the listener to invoke on 'bounds_changed' event
         */
        onBoundsChanged : function(map, listener) {
            google.maps.Event.addListener(map, 'bounds_changed', listener);
        },

        /**
         * Add a 'moveend' listener to Google map
         * 
         * @param map
         *            the map to listen to
         * @param listener
         *            the listener to invoike on 'moveend' event
         */
        onMoveEnd : function(map, listener) {
            google.maps.Event.addListener(map, 'moveend', listener);
        },

        /**
         * Adds a 'click' listener to a Google marker or map
         * 
         * @param mapOrMarker
         *            the object to listen to 'click' events on
         * @param listener
         *            the listener to invoke on 'click' event
         */
        onClick : function(mapOrMarker, listener) {
            google.maps.Event.addListener(mapOrMarker, 'click', listener);
        },

        /**
         * Attaches 'zoom_changed' event listener to a map
         * 
         * @param map
         *            the map to attach event listener to
         * @param listener
         *            the event listener
         */
        onZoomChanged : function(map, listener) {
            google.maps.Event.addListener(map, 'zoomend', listener);
        },

        /**
         * Attaches 'dragstart' event listener to a map
         * 
         * @param map
         *            the map to attach event listener to
         * @param listener
         *            the event listener
         */
        onDragend : function(map, listener) {
            google.maps.Event.addListener(map, 'dragend', listener);
        },

        /**
         * Attaches 'dragend' event listener to a map
         * 
         * @param map
         *            the map to attach event listener to
         * @param listener
         *            the event listener
         */
        onDragstart : function(map, listener) {
            google.maps.Event.addListener(map, 'dragstart', listener);
        },

        /**
         * Attaches 'dragend' event listener to a map
         * 
         * @param map
         *            the map to attach event listener to
         * @param listener
         *            the event listener
         */
        onMouseMove : function(map, listener) {
            google.maps.Event.addListener(map, 'mousemove', listener);
        },

        /**
         * Clears 'click' event listeners from a map or a marker
         * 
         * @param mapOrMarker
         *            to remove click event listener from
         */
        clearClickListeners : function(mapOrMarker) {
            if (mapOrMarker) {
                google.maps.Event.clearListeners(mapOrMarker, 'click');
            }
        }

    };
})();
