/**
 * Map wrapper - loads 9 boxes worth of markers around the current map
 */
/*
 * @param center google.maps.LatLng for centre of map
 * @param MarkerService - gets markers from a server 
 * @param options hash of options
 *      boxScale = ratio of background marker squares to map size
 *      infoService = url for information on individual markers: takes type,id and returns html 
 *      iconFactory = iconFactory object
 */ 
gzo.MarkerLoader = function(Map, MarkerService, Opts)
{
    var me = this;

    this.map = Map;
    this.MarkerService = MarkerService;

    /* store for markers added to map key:id */
    this.addedMarkers = {};

    this.opts = Opts || {};
    this.opts.boxScale = this.opts.boxScale || 0.7;
    this.opts.noClusterZoom = this.opts.noClusterZoom || 17; /* zoom at which clustering stops */
    this.defaultMarkerId = this.opts.defaultMarkerId || false; /* id of default marker */
    this.selectedMarkerId = false;
    this.summaryDisplay = this.opts.summaryDisplay || new gzo.MarkerLoader.popupSummary();

    /* assume that we have a #mapcontainer with #map and #sidebar (with #maphelp, #markersummary) */
    this.jqContainer = jQuery('#mapcontainer');
    this.jqMap = this.jqContainer.children('#map');
    this.jqMapSidebar = this.jqContainer.children('#mapsidebar');

    /* return default location object if it exists and has been added */
    this.getDefaultLocationOb = function()
    {
        if(this.defaultMarkerId && this.addedMarkers[this.defaultMarkerId]) {
            return this.addedMarkers[this.defaultMarkerId].gzo_location_ob;
        }
        return false;
    };

    this.getSelectedLocationOb = function()
    {
        if(this.addedMarkers[this.selectedMarkerId]) {
            return this.addedMarkers[this.selectedMarkerId].gzo_location_ob;
        }
        return false;
    };

    this.setSelectedMarkerId = function(MarkerId) 
    {
        var previous = this.getSelectedLocationOb();
        if(previous) { previous.highlightOff(); }
        this.selectedMarkerId = MarkerId;
    };

    /* regular actions, changing highlit marker, keeping info up to date,etc 
     * NB. callback so don't use "this"
     */
    this.pulse = function()
    {
        /* if there isn't a selected marker, select the default */
        if (!me.selectedMarkerId){ 
            var loc_ob = me.getDefaultLocationOb();
            if(loc_ob) {me.selectedMarkerId = loc_ob.id;}
        }

        /* otherwise do stuff with the selected marker*/
        var selected = me.getSelectedLocationOb();
        if(selected) { 
            selected.toggleHighlight(); 
        }
    };

    this.pulseInterval = setInterval(this.pulse, 1000);

    /* change summary content */
    this.changeSummary = function(content,locationObject) {
        this.summaryDisplay.change(content,locationObject);
    };

    this.moveListener = google.maps.Event.addListener(this.map,'moveend',
            function(){ me.loadMarkers();}
    );

    this.zoomListener = google.maps.Event.addListener(this.map,'zoomend',
            function(){me.clearMarkers();}
    );

    this.clickListener = google.maps.Event.addListener(this.map,'click', 
            function(overlay,latlng,overlaylatlng) {
                /* if there is an overlay and it is one of ours then handle the click */
                if (overlay && overlay.gzo_location_ob){ 
                    overlay.gzo_location_ob.handleClick(me);        
                }
            }
    );

    this.rightClickListener = google.maps.Event.addListener(this.map,'singlerightclick',
        function(overlay,latlng,overlaylatlng) { console.debug(overlaylatlng.gzo_location_ob); }
    );

    /* @param array of reals {swlat,swlng,nelat,nelng} */
    this.fitToSwne = function(swne)
    {
        // padding is based on the distance between the two corners
        var padding_factor = 0.1; /* one tenth of the distance between points */
        var lat_padding = padding_factor * Math.abs(swne[2] - swne[0]);
        var lng_padding = padding_factor * Math.abs(swne[3] - swne[1]); 

        var sw_bound = new google.maps.LatLng(swne[0]-lat_padding,swne[1]-lng_padding);
        var ne_bound = new google.maps.LatLng(swne[2]+lat_padding,swne[3]+lng_padding);

        var bounds = new google.maps.LatLngBounds();
        bounds.extend(sw_bound);
        bounds.extend(ne_bound);
        this.map.setCenter(bounds.getCenter(), this.map.getBoundsZoomLevel(bounds));
      
        if(false) { 
            this.drawBox(swne);
            this.drawBox([sw_bound.lat(),sw_bound.lng(),ne_bound.lat(),ne_bound.lng()], '#0000FF');
        }
    };

    /* draw boxes on map for debugging */
    this.drawBox = function(swne,color)
    {
        var weight = 1;
        if(!color) { color = '#FF0000';}

        this.map.addOverlay(new google.maps.Polyline(
            [
            new google.maps.LatLng(swne[0],swne[1]),
            new google.maps.LatLng(swne[0],swne[3]),
            new google.maps.LatLng(swne[2],swne[3]),
            new google.maps.LatLng(swne[2],swne[1]),
            new google.maps.LatLng(swne[0],swne[1])
            ],
            color, weight)
        );
    };

    /* clear all markers from the map and the marker service */ 
    this.clearMarkers = function() {
        this.map.clearOverlays();
        this.MarkerService.clearMarkers();
        this.addedMarkers = {};
    };

    /* width of the map in decimal degrees - use for boxes because degrees of lng dont change */
    this.degreeWidth = function()
    {
        var b = this.map.getBounds();
        return Math.abs(b.getNorthEast().lng() - b.getSouthWest().lng());
    };

    /* height of the map in decimal degrees */
    this.degreeHeight = function()
    {
        var b = this.map.getBounds();
        return Math.abs(b.getNorthEast().lat() - b.getSouthWest().lat());
    };



    /* calculate the grid of 9 surrounding boxes and get the markers for them */
    this.loadMarkers = function() 
    {
        /* want six decimals for lat,lng and scale */
        var precision6 = function(N) { return Math.round(N*1000000)/1000000; };

        /* use lng because lat changes d'oh */
        var scale = precision6(Math.abs(this.degreeWidth())* this.opts.boxScale);
        var c = this.map.getCenter();
        var y = Math.floor(c.lat()/scale) * scale;
        var x = Math.floor(c.lng()/scale) * scale;

        /* get 3 x 3 surrounding boxes */
        var box_requests = [];
        for(var i=-1;i<2;i++) {
            for(var j=-1;j<2;j++) {
                box_requests.push(this.boxRequest(precision6(scale*i+y),precision6(scale*j+x),scale));
            }
        }
        /* load nine boxes with MarkerService */
        this.MarkerService.getMarkers(box_requests, this.addMarkersToMap);
    };

    /* used as callback by marker service when there are markers to add */
    this.addMarkersToMap = function(Markers)
    {
        if(!Markers) { return; }
        for(i=0; i<Markers.length; i++) {
            me.map.addOverlay(Markers[i]);
            me.addedMarkers[Markers[i].gzo_location_ob.id] = Markers[i];
        }
    };

    /* messenger object - key used to avoid reloading existing boxes */
    this.boxRequest = function(lat,lng,scale) 
    {
        if(false) {this.drawBox([lat,lng,lat+scale,lng+scale]);}

        var clustering = this.map.getZoom() < 
            Math.min(this.opts.noClusterZoom, this.map.getCurrentMapType().getMaximumResolution());

        return {
            rq:{lat:lat,lng:lng,scale:scale,clusters:clustering},
            key:[lat,lng,scale].join('_')
        };
    };

    gzo.addLoadingSpinner(jQuery(this.map.getContainer()));
};


/* add a "loading" image for ajax functions */
gzo.addLoadingSpinner = function(jqContainer)
{
    jQuery('<img id="loading" src="/images/redloader.gif">')
        .css({
            position:"absolute",
            bottom:Math.floor(jqContainer.height()/2),
            right:Math.floor(jqContainer.width()/2)
        })    
        .hide()
        .ajaxStart(function(){jQuery(this).show();})
        .ajaxStop(function(){jQuery(this).fadeOut('slow');})
        .appendTo(jqContainer);
};

gzo.MarkerLoader.defaultIconFactory = function()
{
    this.HOTEL = '/images/mapstuff/green.png'; 
    this.CLUSTER = '/images/mapstuff/red.png'; 
    this.get = function(iconImage) 
    {
        var icon = new google.maps.Icon();
        icon.image = iconImage;
        icon.iconSize = new google.maps.Size(27,37);
        icon.iconAnchor = new google.maps.Point(12,35);
        icon.transparent = '/images/mapstuff/lollypop-trans.png';
        icon.imageMap = [8,1,18,1,23,7,23,17,18,22,6,22,2,17,2,7];
        return icon;
    };
};

/* SummaryDisplay in popup */
gzo.MarkerLoader.popupSummary = function()
{
    this.change = function(content,locationObject)
    {
        console.debug(locationObject);
        jQuery(content).dialog({
            title: locationObject.name,
            modal:true,
            buttons: { "Close": function() { $(this).dialog("close"); } }
        });
    };
};

/* SummaryDisplay that annoyingly slides info up and down */
gzo.MarkerLoader.slidySummary = function()
{
    this.jqSidebarSummary = jQuery('#markersummary');

    /* controls for any summary information */
    this.summaryLoaded = false;
    this.summaryHidden = true;

    this.change = function(content)
    {
        this.jqSidebarSummary.html(content);
        this.summaryLoaded = true;
        this.showSummary();
    };


    /* hide the summary section */
    this.hideSummary = function(){ 
        this.summaryHidden = true;
        this.jqSidebarSummary.slideUp('slow'); 
    };

    /* show the summary section */
    this.showSummary = function(){
        if(this.summaryHidden && this.summaryLoaded) {
            this.jqSidebarSummary.slideDown('slow');
            this.summaryHidden = false;
        }
    };

    /* prepare for change of summary content */
    this.unloadSummary = function() {
        this.summaryLoaded = false;
        this.hideSummary();
    };

};

