
var map;
var geocoder;
var rootMarkers;
var waitingForRootMarkers = false;
var initializingMap = true;
var theJsonUrl;
var theDetailUrl;
var theSelectedTabIndex;
var theSelectedPropertyId;
var theProperties;
var theSelectedPropertyMarker;
var theSelectedPropertyZoom;
var theGSmallMapControl;
var theGMapTypeControl;

var theAddress;

var myZoomIncrease = 2;
var myMinZoom = 1;
var myMaxZoom = 17;
var myOpenInfoWindowZoom = 14;
var myIconCount = 100;
var myOpenInfoWindowZoomSingle = 11;

var theMode;


function initMapWithJson(jsonUrl, detailUrl, googleKey, selectedTabIndex, selectedPropertyId, selectedPropertyZoom) {
    theMode = 'json';

    theProperties = null;
    theJsonUrl = jsonUrl;
    theSelectedPropertyId = selectedPropertyId;
    theSelectedPropertyZoom = selectedPropertyZoom;

    initMapsApiLoader(detailUrl, googleKey, selectedTabIndex);
}

function initMapWithoutAddress(googleKey) {
    theMode = 'address';

    theAddress = null;

    if (map == null) {
        initMapsApiLoader(null, googleKey, null);
    } else {
        openAddressWindow(theAddress);
    }
}

function initMapWithAddress(address, googleKey) {
    theMode = 'address';
    theAddress = address;

    if (map == null) {
        initMapsApiLoader(null, googleKey, null);
    } else {
        openAddressWindow(theAddress);
    }
}

function initMapWithProperties(properties, detailUrl, googleKey, selectedTabIndex, selectedPropertyId, selectedPropertyZoom) {
    theMode = 'properties';

    theProperties = properties;
    theJsonUrl = null;
    theSelectedPropertyId = selectedPropertyId;
    theSelectedPropertyZoom = selectedPropertyZoom;

    initMapsApiLoader(detailUrl, googleKey, selectedTabIndex);
}

// This function adds the script to load the maps API loader
function initMapsApiLoader(detailUrl, googleKey, selectedTabIndex) {
    theDetailUrl = detailUrl;

    theSelectedTabIndex = selectedTabIndex;

    // include the Google maps javascript
    $("head").append('<script src="http://www.google.com/jsapi?key=' + googleKey + '&callback=loadMapsApi" type="text/javascript"/>');
}

// Load the Google Maps API
function loadMapsApi() {
    google.load("maps", "2", {"callback" : mapsApiLoaded});
}

function mapsApiLoaded() {
    var mapsJavascriptUrl = contextPath + '/js/extinfowindow.js';
    $("head").append('<script src="' + mapsJavascriptUrl + '" type="text/javascript"/>');

    // NOTE: This could be moved to the end of the extinfowindow.js file when it causes
    // problems (e.g. when the javascript has not yet been loaded before we call 'downloadProperties()'
    downloadProperties();
}

function downloadProperties() {
    if (theMode == 'json') {
        GDownloadUrl(theJsonUrl, readProperties);
    } else if (theMode == 'address') {
        showMap();
    } else if (theMode == 'properties') {
        processProperties();
    } else {
        alert('Invalid mode: ' + theMode);
    }
}

function showMap() {
    if (theMode == 'address') {
        if (GBrowserIsCompatible()) {
            setMap();
            configureMapAddress();
        }
    } else if (!initializingMap) {
        if (GBrowserIsCompatible()) {
            
            if ($('#subTabContainer ul').data('selected.tabs') == theSelectedTabIndex) {
                setMap();

                waitingForRootMarkers = true;

                if (rootMarkers != null)  {
                    configureMap();
                } else {
                    $("#mapContainer").css("backgroundImage", "url('../images/pleaseWait/rotatingPleaseWait.gif')");
                }
            }
        }
    }
}

function setMap() {
    map = new GMap2(document.getElementById("mapContainer"));
    geocoder = new GClientGeocoder();

    $("#mapContainer").css("backgroundColor", "white");
    $("#mapContainer").css("backgroundRepeat", "no-repeat");
    $("#mapContainer").css("backgroundPosition", "center");

    map.removeMapType(G_SATELLITE_MAP);

    theGSmallMapControl = new GSmallMapControl();
    theGMapTypeControl = new GMapTypeControl();

    if (('address' == theMode)) {
        map.disableDragging();
    } else {
        map.addControl(theGSmallMapControl);
        map.addControl(theGMapTypeControl);
    }
}


function configureMap() {
    var initialZoom;
    var center;

    if (theSelectedPropertyMarker == null) {
        if (rootMarkers.length > 1)
        {
            center = new GLatLng(0, 0);
            initialZoom = myMinZoom;
        }
        else
        {
            // calculate the initial zoom level and center by calculating the latLng bounds of
            // all properties and use getBoundsZoomLevel method to calculate the zoom level
            var latLngBounds = calculateLatLngBounds(theProperties);
            initialZoom = Math.min(map.getBoundsZoomLevel(latLngBounds), myOpenInfoWindowZoom - myZoomIncrease);
            center = latLngBounds.getCenter();
        }
    }
    else
    {
        center = theSelectedPropertyMarker.getLatLng();

        if (theSelectedPropertyZoom) {
            initialZoom = theSelectedPropertyZoom;
        } else {
            initialZoom = Math.min(theSelectedPropertyMarker['zoom'], myOpenInfoWindowZoom);
        }
    }

    map.setCenter(center);
    map.setZoom(initialZoom);
    
    $("#mapContainer").css("backgroundImage", "");

    var markerManager = createMarkerManager(rootMarkers);

    GEvent.addListener(map, "click", handleClick);

    if (theSelectedPropertyMarker != null)
    {
        openInfoWindow(theSelectedPropertyMarker);
    }
}

function calculateLatLngBounds(properties) {

    var minLat;
    var maxLat;
    var minLng;
    var maxLng;

    for (var i = properties.length - 1; i >= 0; i--) {
        var property = properties[i];
        minLat = !minLat ? property.geoLatitude : Math.min(property.geoLatitude, minLat);
        maxLat = !maxLat ? property.geoLatitude : Math.max(property.geoLatitude, maxLat);
        minLng = !minLng ? property.geoLongitude : Math.min(property.geoLongitude, minLng);
        maxLng = !maxLng ? property.geoLongitude : Math.max(property.geoLongitude, maxLng);
    }

    // Define the two corners of the bounding box
    var sw = new GLatLng(minLat, minLng);
    var ne = new GLatLng(maxLat, maxLng);

    // Create a bounding box
    return new GLatLngBounds(sw, ne);
}

function configureMapAddress() {
    openAddressWindow(theAddress);
}
          /*
function handleMouseOver(point) {
    if (!this['polygon']) {

        var bounds = createBoundsFromMarkers(this['markers']);

        var sw = bounds.getSouthWest();
        var ne = bounds.getNorthEast();

        var nw = new GLatLng(ne.lat(), sw.lng());
        var se = new GLatLng(sw.lat(), ne.lng());

        var polygon = new GPolygon([sw, nw, ne, se, sw], '#000000', 1, 0, '#555555', 0.5);

        this['polygon'] = polygon;

        map.addOverlay(polygon);
    }
}

function handleMouseOut(point)
{
   if (this['polygon']) {
       map.removeOverlay(this['polygon']);
       this['polygon'] = null;
   }
}
   */
function handleClick(marker, point)
{
   if (marker && marker['propertyCount'])
   {
       var propertyCount = marker['propertyCount'];
       var currentZoom = map.getZoom();
       var center;

       if (propertyCount > 1)
       {
           var markers = marker['markers'];

           var markerZoom;

           if (markers != null)
           {
               var bounds = createBoundsFromMarkers(markers);

               center = bounds.getCenter();
               markerZoom = map.getBoundsZoomLevel(bounds);
           }
           else
           {
               // This marker is on the lowest level
               center = marker.getLatLng();
               markerZoom = myMaxZoom;
           }

           if (currentZoom >= myOpenInfoWindowZoom)
           {
               openInfoWindow(marker);
           }
           else
           {
               if (map.getExtInfoWindow() != null) {
                  map.closeExtInfoWindow();
               }

               var nextZoom = currentZoom + myZoomIncrease;

               nextZoom = Math.min(nextZoom, markerZoom);

               if (nextZoom != currentZoom)
               {
                   map.setCenter(center, nextZoom);
               }
               else
               {
                   map.setCenter(center);
               }
           }
       }
       else
       {
           if (currentZoom >= myOpenInfoWindowZoomSingle)
           {
               openInfoWindow(marker);
           }
           else
           {
               center = marker.getLatLng();
               var nextZoom = currentZoom + myZoomIncrease;

               map.setCenter(center, nextZoom);
           }
       }
   }
}

// ================================================================
 // === Define the function thats going to process the JSON file ===
function readProperties(doc) {
    var jsonData = eval('(' + doc + ')');

    theProperties = jsonData.properties;

    processProperties();
}

function processProperties() {
    rootMarkers = createRootMarkers(theProperties);

    initializingMap = false;

    if (waitingForRootMarkers) {
        configureMap();
    } else {
        showMap();
    }
}

function openInfoWindow(marker)
{
    var properties = marker.properties;

    var detailUrl = theDetailUrl + (theDetailUrl.indexOf('?') < 0 ? '?' : '&') + ['mapZoom', map.getZoom()].join('=');
    var divId = 'infoContents';
    var propertyCount = 0;

    var propertyIdArrays = marker['propertyIdArrays'];

    for (var i = 0; i < propertyIdArrays.length; i++) {
        var propertyIdArray = propertyIdArrays[i];

        for (var j = 0; j < propertyIdArray.length; j++) {
            var propertyId = propertyIdArray[j];

            detailUrl += '&propertyId=' + propertyId;
            divId += '_' + propertyId;
            propertyCount++;
        }
    }

    var windowStyle;
    var windowWidth;
    var windowHeight;

    if (propertyCount > 1) {
        windowStyle = "map_details_multiple_window";
        windowWidth = 276;
        windowHeight = 117;
    } else {
        windowStyle = "map_details_single_window";
        windowWidth = 260;
        windowHeight = 77;
    }

    var contents = "<div id=\"" + divId + "\" style=\"width:" + windowWidth + "px;height:" + windowHeight + "px;\" src=\"../images/pleaseWait/rotatingPleaseWait.gif\"/></div>";

    marker.openExtInfoWindow(
        map,
        windowStyle,
        contents,
        {
            ajaxUrl: detailUrl,
            beakOffset: 3,
            paddingX: 25,
            paddingY: 25
        }
    );
}

function openAddressWindow(address) {

    if (address != null) {

        var point = new GLatLng(address.geoLatitude, address.geoLongitude);

        map.setCenter(point, 16);

        var marker = new GMarker(point, {icon: createMapsPointer(1), clickable: false});

        map.clearOverlays();
        map.addOverlay(marker);

    } else {
        map.setCenter(new GLatLng(52.119999,5.537109), 6);
    }
}

function createIcons()
{
    var icons = [];

    for (var i = 1; i <= myIconCount; i++)
    {
        icons.push(createMapsPointer('' + i));
    }

    return icons;
}

function createRootMarkers(properties) {
    var icons = createIcons();

    var zoom = myMaxZoom;
    var selectedPropertyZoom = theSelectedPropertyZoom;

    var groupLats = createGroupLatsFromProperties(properties, zoom, icons, selectedPropertyZoom == zoom, theSelectedPropertyId);

    while(zoom > myMinZoom) {
        groupLats = processGroupLats(groupLats, --zoom, icons, selectedPropertyZoom == zoom);
    }

    var rootMarkers;

    for(var baseLat in groupLats) {
        var groupLngs = groupLats[baseLat];

        for (var baseLng in groupLngs)
        {
            rootMarkers = groupLngs[baseLng];
        }
    }

    return rootMarkers;
}

function createMarkerManager(rootMarkers)
{
    // create a marker manager
    var markerManager = new GMarkerManager(map, {borderPadding:1});

    addMarkers(markerManager, rootMarkers, myMinZoom, addMarkers);

    markerManager.refresh();

    return markerManager;
}

function addMarkers(markerManager, markers, fromZoom, addMarkersFunc)
{
    for (var k = markers.length - 1; k >= 0; k--)
    {
        var marker = markers[k];

        var toZoom = marker['zoom'];

        markerManager.addMarkers(
            [ marker ],
            fromZoom,
            toZoom);

        var subMarkers = marker['markers'];

        if (subMarkers != null)
        {
            addMarkersFunc(markerManager, subMarkers, toZoom + 1, addMarkersFunc);
        }
    }
}

function createMapsPointer(postFix)
{
    var icon = new GIcon();
    icon.image = contextPath + "/images/icons/mapspointer/" + postFix + ".png";
    icon.iconSize = new GSize(30, 30);
    icon.infoWindowAnchor = new GPoint(15, 5);
    icon.iconAnchor = new GPoint(15, 15);
    icon.imageMap = [23,43, 7,31, 7,27, 3,23, 15,6, 21,8, 33,4, 45,23, 41,26, 39,31];

    return icon;
}

function createGroupLatsFromProperties(properties, zoom, icons, isSelectedZoom, selectedPropertyId)
{
    var groupLats = new Object();

    var createMarkerFunc = createMarker;

    var GLatLngFunc = GLatLng;
    var GMarkerFunc = GMarker;

    for (var i = properties.length - 1; i >= 0; i--)
    {
       var property = properties[i];
      
       var baseLat = (property.geoLatitude * 1200) | 0;
       var baseLng = (property.geoLongitude * 600) | 0;

       var groupLngs = groupLats[baseLat];

       var marker;

       if (groupLngs == null)
       {
           var center = new GLatLngFunc(property.geoLatitude, property.geoLongitude);
           marker = createMarkerFunc(center, null, zoom, 1, [[ property.propertyId ]], icons, GMarkerFunc);
           marker['properties'] = [ property ];

           var group = [ marker ];

           groupLngs = new Object();
           groupLngs[baseLng] = group;

           groupLats[baseLat] = groupLngs;
       }
       else
       {
           var group = groupLngs[baseLng];

           if (group == null)
           {
               var center = new GLatLngFunc(property.geoLatitude, property.geoLongitude);
               marker = createMarkerFunc(center, null, zoom, 1, [[ property.propertyId ]], icons, GMarkerFunc);
               marker['properties'] = [ property ];

               group = [ marker ];
               groupLngs[baseLng] = group;
           }
           else
           {
               group[0] = updateMarkerFromProperty(group[0], property, icons, GMarkerFunc);

               marker = group[0];
           }
       }

       if (selectedPropertyId) {
           if (selectedPropertyId == property.propertyId) {
               marker['selected'] = true;
               if (isSelectedZoom) {
                   theSelectedPropertyMarker = marker;
               }
           }
       }
    }

    return groupLats;
}

function reportGroupLats(groupLats, maxAlerts)
{
    var alertCount = 0;

    for(var baseLat in groupLats)
    {
        var groupLngs = groupLats[baseLat];

        for (var baseLng in groupLngs)
        {
            var object = groupLngs[baseLng];

            alert('groupLats ' + groupLats.length + ', groupLngs ' + groupLngs.length + ', lat ' + baseLat + ', lng ' + baseLng + ' : ' + object);

            alertCount++;

            if (alertCount == maxAlerts)
            {
                alert('return');

                return;
            }
        }
    }

    alert('done');
}

function updateMarkerFromProperty(marker, property, icons, GMarkerFunc)
{
    var properties = marker['properties'];

    properties.push(property);

    var center = createCenterFromProperties(properties);

    var propertyIdArrays = marker['propertyIdArrays'];

    propertyIdArrays.push([ property.propertyId ]);

    var propertyCount = marker['propertyCount'] + 1;

    var marker = createMarker(center, null, myMaxZoom, propertyCount, propertyIdArrays, icons, GMarkerFunc);

    marker['properties'] = properties;

    return marker;
}

function processGroupLats(groupLats, zoom, icons, isSelectedZoom)
{
     var nextGroupLats = new Object();

     var createMarkerFromMarkersFunc = createMarkerFromMarkers;
     var createMarkerFunc = createMarker;
     var GMarkerFunc = GMarker;

     for(var baseLat in groupLats)
     {
         var nextBaseLat = baseLat >>> 1;
         var nextGroupLngs = nextGroupLats[nextBaseLat];

         if (nextGroupLngs == null)
         {
             nextGroupLngs = new Object();
             nextGroupLats[nextBaseLat] = nextGroupLngs;
         }

         var groupLngs = groupLats[baseLat];

         for (var baseLng in groupLngs)
         {
             var nextBaseLng = baseLng >>> 1;
             var nextGroup = nextGroupLngs[nextBaseLng];

             if (nextGroup == null)
             {
                 nextGroup = [];
                 nextGroupLngs[nextBaseLng] = nextGroup;
             }

             var groupMarkers = groupLngs[baseLng];

             var marker;

             if (groupMarkers.length == 1)
             {
                 marker = groupMarkers[0];
             }
             else
             {
                 marker = createMarkerFromMarkersFunc(groupMarkers, zoom, icons, GMarkerFunc, createMarkerFunc);
             }

             if (isSelectedZoom)
             {
                 if (marker['selected'] == true)
                 {
                     theSelectedPropertyMarker = marker;
                 }
             }

             nextGroup.push(marker);
         }

     }

     return nextGroupLats;
}

function createMarkerFromMarkers(markers, zoom, icons, GMarkerFunc, createMarkerFunc)
{
    var firstMarker = markers[0];

    var propertyCount = firstMarker['propertyCount'];
    var propertyIdArrays;

    var firstLatLng = firstMarker.getLatLng();

    var mustAddPropertyIdArrays = zoom >= myOpenInfoWindowZoom;
    if (mustAddPropertyIdArrays)
    {
       propertyIdArrays = firstMarker['propertyIdArrays'];
    }

    var firstLat = firstLatLng.lat();
    var firstLng = firstLatLng.lng();

    var minLat = firstLat;
    var maxLat = firstLat;
    var minLng = firstLng;
    var maxLng = firstLng;

    var selected = false;

    for (var i = markers.length - 1; i > 0; i--)
    {
        var marker = markers[i];

        propertyCount += marker['propertyCount'];
        if (mustAddPropertyIdArrays)
        {
           propertyIdArrays = propertyIdArrays.concat(marker['propertyIdArrays']);
        }

        var latLng = marker.getLatLng();

        var lat = latLng.lat();
        var lng = latLng.lng();

        if (minLat > lat )
        {
            minLat = lat;
        }
        else if (maxLat < lat)
        {
            maxLat = lat;
        }

        if (minLng > lng )
        {
            minLng = lng;
        }
        else if (maxLng < lng)
        {
            maxLng = lng;
        }

        selected |= marker['selected'];
    }

    var center = new GLatLng((minLat + maxLat) / 2, (minLng + maxLng) / 2);

    return createMarkerFunc(center, markers, zoom, propertyCount, propertyIdArrays, icons, GMarkerFunc, selected);
}

function createMarker(center, markers, zoom, propertyCount, propertyIdArrays, icons, GMarkerFunc, selected)
{
    var iconNumber = propertyCount;

    if (iconNumber > myIconCount)
    {
        iconNumber = myIconCount;
    }

    var marker = new GMarkerFunc(center, icons[iconNumber - 1]);

    marker['markers'] = markers;
    marker['zoom'] = zoom;
    marker['propertyCount'] = propertyCount;
    if (propertyIdArrays != null)
    {
        marker['propertyIdArrays'] = propertyIdArrays;
    }
    marker['selected'] = selected;
/*
    if (propertyCount > 1)
    {
        GEvent.addListener(marker, "mouseover", handleMouseOver);
        GEvent.addListener(marker, "mouseout", handleMouseOut);
    }
*/
    return marker;
}



function createCenterFromProperties(properties)
{
    var minLat = properties[0].geoLatitude;
    var maxLat = properties[0].geoLatitude;
    var minLng = properties[0].geoLongitude;
    var maxLng = properties[0].geoLongitude;

    for (var i = 1; i < properties.length; i++)
    {
        var lat = properties[i].geoLatitude;
        var lng = properties[i].geoLongitude;

        if (minLat > lat )
        {
            minLat = lat;
        }
        else if (maxLat < lat)
        {
            maxLat = lat;
        }

        if (minLng > lng )
        {
            minLng = lng;
        }
        else if (maxLng < lng)
        {
            maxLng = lng;
        }
    }

    center = new GLatLng((minLat + maxLat) / 2, (minLng + maxLng) / 2);

    return center;
}

function createBoundsFromProperties(properties)
{
    // create bounds to calculate outer bounds and center
    var bounds = new GLatLngBounds();
    var propertiesLength = properties.length;


    // extend bounds with all points
    for (var i = 0; i < propertiesLength; i++)
    {
        var property = properties[i];

        var latLng = new GLatLng(property.geoLatitude, property.geoLongitude);

        bounds.extend(latLng);
    }

    return bounds;
}

function createBoundsFromMarkers(markers)
{
    // create bounds to calculate outer bounds and center
    var bounds = new GLatLngBounds();

    var markersLength = markers.length;

    // extend bounds with all points
    for (var i = 0; i < markersLength; i++)
    {
        var marker = markers[i];

        var latLng = marker.getLatLng();

        bounds.extend(latLng);
    }

    return bounds;
}

function openDetails(url) {
    alert('url ' + url);
    window.open(url + "&mapZoom=" + map.getZoom(), "_self");
}
