(function($){
    $.fn.geocode = function(options) {
        var opts = $.extend({
                mapZoom: 8,
                mapCenter: new GLatLng(0, 0),
                init: true,
                searchOnChange: true
            }, options),
            addressField = opts.addressField || $(this).find("input[name=address]"),
            locationField = $(this).find("input[name=location]"),
            accuracyField = $(this).find("input[name=accuracy]"),
            nameField = $(this).find("input[name=name]"),
            timeout = null,
            geocoder = new GClientGeocoder();

        if ("locationField" in opts)
           locationField = opts.locationField;

        if (locationField.val()) {
            try {
                var point = JSON.parse(locationField.val());
                opts.mapCenter = new GLatLng(point.coordinates[1], point.coordinates[0]);
                opts.mapZoom = 15;
            } catch(err) {
            }
        }

        if (!opts.map) {
            if (!("mapContainer" in opts)) {
                opts.mapContainer = $("<div></div>").css({
                        width: "600px",
                        height: "500px"
                }).insertAfter(addressField);
            }

            opts.map = new GMap(opts.mapContainer.get(0));
            opts.map.setCenter(opts.mapCenter, opts.mapZoom);
            opts.map.setUIToDefault();
        }

        function codeAddress(callback) {
            address = addressField.val();

            if (address) {
                geocoder.getLocations(address, function(data) {
                    if (data.Status.code == 200 && data.Placemark.length > 0) {

                        var box = data.Placemark[0].ExtendedData.LatLonBox,
                        bounds = new GLatLngBounds(
                            new GLatLng(box.south, box.west),
                            new GLatLng(box.north, box.east)
                        ),
                        point = data.Placemark[0].Point,
                        latlng = new GLatLng(point.coordinates[1], point.coordinates[0]);

                        opts.map.setCenter(bounds.getCenter(), opts.map.getBoundsZoomLevel(bounds));
                        fillLocationField(latlng);
                        if (accuracyField.length) {
                            accuracyField.val(data.Placemark[0].AddressDetails.Accuracy);
                        }
                        if (nameField.length) {
                            nameField.val(data.Placemark[0].address.split(",")[0]);
                        }
                        if (typeof(callback) == 'function') {
							callback(data.Placemark[0]);
						}
                    }
                });
            }
        }

        function fillLocationField(location) {
            locationField.val(
                JSON.stringify({
                    type: "Point",
                    coordinates: [location.lng(), location.lat()]
                })
            );
        }

        if (opts.searchOnChange) {
            addressField.keydown(function(){
                if (timeout) {
                    clearTimeout(timeout);
                }
                timeout = setTimeout(codeAddress, 400);
            });
        }

        if (opts.submitButton && opts.submitButton.length) {
            opts.submitButton.click(function(){
                codeAddress();
                return false
            })
        }

        if (opts.init) codeAddress();

        return {
            opts: opts,
            codeAddress: codeAddress
        }
    };
})(jQuery);

