var loc_geocoder = null;
function loc_getGeocoder()
{
    if ( ! loc_geocoder ) {
        loc_geocoder = new google.maps.Geocoder();
    }
    return loc_geocoder;
}


function loc_lookupLocation( str, callback )
{
    //alert('submitting: '+str);
    var geo = loc_getGeocoder();
    geo.geocode( { address: str }, 
                 function( results, status ) {
                    loc_geocodeCallback( results, status, callback );
                 } );
}

function loc_getFuzzyLocation( lat, lon, callback )
{
    loc_reverseGeocode( lat, lon, function( results ) { 
                            loc_extractFuzzyLoc( results, callback );
                        } );
}

function loc_reverseGeocode( lat, lon, callback )
{
    //alert('submitting: '+lat+', '+lon);
    var geo = loc_getGeocoder();
    geo.geocode( { latLng: new google.maps.LatLng(lat, lon) }, 
                 function( results, status ) {
                    loc_geocodeCallback( results, status, callback );
                 } );

}

function loc_geocodeCallback( results, status, callback )
{
    //alert('geocode callback, status: '+status+', res count='+(results!=null?results.length:'null'));
    
    toReturn = null;
    if ( 'OK' == status ) {
        toReturn = results;
//        alert('got results: '+results+', '+results.length);
        if ( results.length == 1 ) {
            r = results[0];
//            alert('r='+r.formatted_address);
            if ( r.geometry ) {
                loc_reverseGeocode( r.geometry.location.lat(), r.geometry.location.lng() );
            }
        }
    } else if ( 'ZERO_RESULTS' == status ) {
        toReturn = [];
    }
    if ( callback ) {
        callback( toReturn );
    }
}

function loc_extractFuzzyLoc( results, callback )
{
    fuzzyLoc = null;
    //alert('extract fuzzy, results='+(results?results.length:'null'));
    if ( results != null )
    {
        var len = results.length;
        for ( var i = 0; i < len; i++ ) 
        {
            r = results[i];
            types = r.types;
            //alert('examine: '+types);
            if ( ( index = $.inArray( 'street_address', types )  ) == -1 ) {
                fuzzyLoc = r;
                break;
            } 
        }
    }

    if ( callback ) {
        callback( fuzzyLoc );
        //alert('return fuzzy: '+fuzzyLoc);
    }
}

function loc_extractLocationData( address )
{
    var lat = null;
    var lon = null;
    var formattedAddress = null;
    var shortDescription = null;
    var country = null;

    if ( address && address.address_components )
    {
        lat = address.geometry.location.lat();
        lon =  address.geometry.location.lng();
        formattedAddress = address.formatted_address;

        var len = address.address_components.length;
        var r;
        var types;
        var shortState;
        var longState;
        var city;
        var subLocale;

        for ( var i = 0; i < len; i++ ) 
        {
            r = address.address_components[i];
            types = r.types;
            if ( ( index = $.inArray( 'country', types )  ) != -1 ) {
                country = r.short_name;
            } 
            else if ( ( index = $.inArray( 'administrative_area_level_1', types )  ) != -1 ) {
                shortState = r.short_name;
                longState = r.long_name;
            }
            else if ( ( index = $.inArray( 'locality', types )  ) != -1 ) {
                city = r.long_name;
            }
            else if ( ( index = $.inArray( 'sublocality', types )  ) != -1 ) {
                subLocale = r.long_name;
            }
        }

        if ( city || subLocale ) {
            shortDescription = ( subLocale ? subLocale : city ) + ', ' + shortState;
        } else {
            shortDescription = longState + ', ' + country;
        }
    }

    return {
        'lat': lat,
        'lng': lon,
        'address': formattedAddress,
        'country': country,
        'shortDescription': shortDescription
    };
}

var loc_maps = {};
function loc_createMap( element, lat, lon, markerText, callback, zoom )
{
    var data = loc_maps[element.id];
    var map = null;
    var loc = new google.maps.LatLng( lat, lon );
    if ( ! zoom ) { zoom = 15; }
    if ( data )
    {
        //alert('existing map');
        if ( data.infoWindows ) 
        {
            for ( var i = 0; i < data.infoWindows.length; i++ ) {
                data.infoWindows[ i ].close();
            }
            data.infoWindows = null;
        }
        if ( data.markers )
        {
            for ( var i = 0; i < data.markers.length; i++ ) {
                var m = data.markers[ i ];
                google.maps.event.clearInstanceListeners(m);
                m.setMap(null);
            }
            data.markers = null;
        }
        map = data.map;
        map.panTo( loc );
        map.setZoom( zoom );
    }
    else
    {
        //alert('create new map');
        var options = {
            zoom: zoom,
            mapTypeControl: false,
            center: loc,
            mapTypeId: google.maps.MapTypeId.ROADMAP
        };
 
        map = new google.maps.Map( element, options );
        google.maps.event.addListener(map, 'click', function() {
            var d = loc_maps[ map.getDiv().id ];
            if ( d.activeInfoWindow ) {
                d.activeInfoWindow.close();
                d.activeInfoWindow = null;
            }
        });

        data = { map: map };
    }
 
    var marker = null;
    if ( markerText ) 
    {
        marker = new google.maps.Marker({
            position: loc, 
            map: map, 
            title:markerText
        });
 
    }

    if ( marker ) {
        data.markers = [ marker ];
    }

    loc_maps[element.id] = data;

    return map;
}

function loc_addMarker( map, lat, lon, title, windowContent, imageUrl )
{
    //alert('add marker: '+lat+', '+lon+', '+title+', '+windowContent);
    var data = loc_maps[map.getDiv().id];

    var infoWindow = null;
    if ( windowContent )
    {
        infoWindow = new google.maps.InfoWindow({
            content: windowContent
        });
    }

    var loc = new google.maps.LatLng( lat, lon );
    var moptions = {
            position: loc, 
            map: map, 
            title:title
    };
    if ( imageUrl ) {
        moptions[ 'icon' ] = imageUrl;
    }
    var marker = new google.maps.Marker( moptions );

    if ( infoWindow )
    {
        google.maps.event.addListener(marker, 'click', function() {
            var d = loc_maps[ map.getDiv().id ];
            if ( d.activeInfoWindow ) {
                d.activeInfoWindow.close();
                d.activeInfoWindow = null;
            }
            infoWindow.open(map,marker);
            d.activeInfoWindow = infoWindow;
        });

        if ( ! data.infoWindows ) {
            data.infoWindows = [];
        }
        data.infoWindows.push( infoWindow );
    }

    if ( ! data.markers ) {
        data.markers = [];
    }
    data.markers.push( marker );
    return marker;
}

function loc_fitBounds( map )
{
    var data = loc_maps[ map.getDiv().id ];
    if ( data.markers )
    {
        var bounds = new google.maps.LatLngBounds( map.getCenter() );
        for ( var i = 0; i < data.markers.length; i++ )
        {
            bounds.extend( data.markers[ i ].getPosition() );
        }
        map.panToBounds( bounds );
        map.fitBounds( bounds );
    }
}

