/*

Plugin name: UGMOShowClusters
For Ugmo version: 3.0

Description:
This plugin shows clusters it finds in the jsondata.clusters list. These clusters can be generated by UGMOClientClusterer or on the server side.

Settings:
define settings using:
yourMap.UGMOShowClusters.settings

*/

(function(undefined){
    if(typeof UGMO == "undefined"){
        throw("UGMO is required for UGMOShowClusters");
    }
    
    //define a public namespace within UGMO
    UGMO.prototype.UGMOShowClusters = {};
    
    //default settings
    UGMO.prototype.UGMOShowClusters.settings = {
	//show the number of markers
        showNumberOfMarkers: true,
	//icon settings
        icon: {
	    //Path to the image for the icon
            image: null,
	    //icon width
            width: null,
	    //icon height
            height: null,
	    //icon div class name
            className: 'clusterMarker',
	    //anchor to the map, counting from top-left
            anchor: [0, 0],
	    //center the number
	    centerText: true
        },
        clusterClusterMarkersUrl: null
    };
    
    //list to store the clusterMarkers in
    UGMO.prototype.clusters = [];
    
    //object to store the select handler
    UGMO.prototype.UGMOShowClusters.selectClusterMarkerHandler;
    
    //override drawMarkers
    var oldDrawMarkers = UGMO.prototype.drawMarkers;
    UGMO.prototype.drawMarkers = function(jsondata, zoomToExtend){
        var ugmo_ = this;
        
        //remove the old clusters and old markers
        if(this.clusters.length){
            for(i in this.clusters){
                this.clusters[i].setMap(null);
            }
            this.clusters = [];
        }
        this.removeMarkers();
        
        //first draw the normal markers
        oldDrawMarkers.apply(this, [jsondata, zoomToExtend]);
        
        //now we need to see what's left (hopefully some clusters)
        if(jsondata.clusters && jsondata.clusters.length){
            for(i in jsondata.clusters){
                var cluster = jsondata.clusters[i];
                //draw this cluster
                var clusterMarker = drawCluster(ugmo_, cluster);
                this.clusters.push(clusterMarker);
                
                //attach handler on this marker
                this.UGMOShowClusters.selectClusterMarkerHandler = google.maps.event.addDomListener(clusterMarker.div_, "click", function(cluster) {
                    return function(event){
                        event = event || window.event;
                        event.cancelBubble = true;
                        if (event.stopPropagation){
                            event.stopPropagation();
                        }
                        
                        ugmo_.UGMOShowClusters.selectClusterMarker.apply(ugmo_, [cluster]);
                    };
                }(cluster));
            }
        }
    };
    
    /* overridable functions */
    
    //create the "marker", gets called by drawCluster
    UGMO.prototype.UGMOShowClusters.createMarker = function(cluster){
        var div = document.createElement("DIV");
        
        //standard styles
        div.style.cursor = 'pointer';
        
        //check if there are icon settings
        if(!this.settings.icon || typeof this.settings.icon != 'object'){
            throw('Icon settings for UGMOShowClusters are required!');
        }
        
        //check if the image is set
        if(!this.settings.icon.image){
            throw("You need to set an image for the cluster icon in UGMOShowClusters!");
        }
	
        //check if width and height are set
        if(!this.settings.icon.height || typeof this.settings.icon.height != 'number' || !this.settings.icon.width || typeof this.settings.icon.width != 'number'){
            throw("height and width for the icon in settings in UGMOShowClusters are required and con not be 0!");
        }
        
        //check anchor while we're at it (we don't use it here)
        if(!this.settings.icon.anchor || typeof(this.settings.icon.anchor) !== 'object' || this.settings.icon.anchor.length != 2 || this.settings.icon.anchor[0] === null || isNaN(this.settings.icon.anchor[0]) || this.settings.icon.anchor[1] === null || isNaN(this.settings.icon.anchor[1])){
                throw('nachor property of icon "' + this.data.id + '" in settings for UGMOShowClusters needs to be an array with two numbers!');
            } 
        
        div.style.height = this.settings.icon.height + 'px';
        div.style.width = this.settings.icon.width + "px";
        
        //check for a className
        if(this.settings.icon.className){
            div.className = this.settings.icon.className;
        }
        
        //check for an icon image
        if(this.settings.icon.image){           
            div.style.backgroundImage = 'url('+this.settings.icon.image+')';
            div.style.backgroundRepeat = 'no-repeat';
        }
        
        if(this.settings.showNumberOfMarkers){
            var numberOfMarkers = cluster.markers ? cluster.markers.length : cluster.count;
            //put number of markers in
            $(div).text(numberOfMarkers);
            
            //style text
            if(this.settings.icon.centerText){
                div.style.textAlign = "center";
                div.style.verticalAlign = "middle";
                div.style.lineHeight = div.style.height;
            }
        }
        
        return div;
    };
    
    //function that gets called when the clustermarker is clicked
    UGMO.prototype.UGMOShowClusters.selectClusterMarker = function(cluster){
        var ugmo_ = this;
        var currentZoom = this.map.getZoom();
        
        // we need to know wether we're at the maximum zoomlevel:
        
        if( this.map.getMapTypeId() == 'satellite' || this.map.getMapTypeId() == 'hybrid' ){
            // create a maxZoomService class
            var maxZoomService = new google.maps.MaxZoomService();
            
            maxZoomService.getMaxZoomAtLatLng(this.map.getCenter(), function(response){selectClusterMarker(response.zoom)});
        }else{
            var maxZoomAtLocation = this.map.mapTypes[this.map.getMapTypeId()].maxZoom;
            selectClusterMarker(maxZoomAtLocation);
        }
        
        
        // this function gets executed when we know which zoomlevel is the maximum
        function selectClusterMarker(maxZoomAtLocation){
            var maxZoom = Math.min(maxZoomAtLocation, ugmo_.getMaxZoom());
            
            if( currentZoom < maxZoom ){
                zoomToCluster(ugmo_, cluster);
            }else{
                ugmo_.UGMOShowClusters.showClutterCluster.apply(ugmo_, [cluster]);
            }
        }
    };
    
    
    /* internal non-overridable function */
     
    //function to draw the cluster
    var drawCluster = function(ugmo_, cluster){
        var marker = ugmo_.UGMOShowClusters.createMarker(cluster);
        var location = new google.maps.LatLng(cluster.lat, cluster.lon);
        
        var clusterMarker = new ClusterMarker(ugmo_.map, location, marker, ugmo_.UGMOShowClusters.settings.icon.anchor);
        
        return clusterMarker;
    };
    
    //zoom to the markers in the cluster
    var zoomToCluster = function(ugmo_, cluster){
        var markerBounds = new google.maps.LatLngBounds();
        
        if(cluster.markers){
            for(i in cluster.markers){
                markerBounds.extend(new google.maps.LatLng(cluster.markers[i].lat, cluster.markers[i].lon));
            }
        }else{
            markerBounds.extend(new google.maps.LatLng(cluster.bounds[0][0], cluster.bounds[0][1]));
            markerBounds.extend(new google.maps.LatLng(cluster.bounds[1][0], cluster.bounds[1][1]));
        }
        ugmo_.map.fitBounds(markerBounds);
        
    };
    
    // clutter cluster
    //   when there markers in a cluster overlap so they can't be show normally, they have combine there infowindow
    UGMO.prototype.UGMOShowClusters.showClutterCluster = function(cluster){
        var ugmo_ = this;
        
        if( this.UGMOShowClusters.settings.clusterClusterMarkersUrl !== null && typeof(this.UGMOShowClusters.settings.clusterClusterMarkersUrl) !== 'undefined' ){
        
            requestData = {
                minLat: cluster.bounds[0][0],
                minLon: cluster.bounds[0][1],
                maxLat: cluster.bounds[1][0],
                maxLon: cluster.bounds[1][1]
            };
            
            
		    var timeout = ugmo_.settings.getMarkersTimeout;
            
            $.ajax({
                url: this.UGMOShowClusters.settings.clusterClusterMarkersUrl,
                data: requestData,
                dataType: 'json',
                success: function(data) {
                    showClutterCluster(data);
                },
                timeout: timeout,
                error: function(xhr, ajaxOptions, thrownError) {
                    if( typeof(console) !== 'undefined' ){
                        console.warn("UGMOShowClusters.settings.clusterClusterMarkersUrl not reachable, can show cluttered markers!");
                    }
                    showClutterCluster({html:"<div style=\"height: 100px;\"><h2>Op deze plek is meer dan één item aanwezig.</h2></div>"});
                }
            });
            
        }else{
            if( typeof(console) !== 'undefined' ){
                console.warn("UGMOShowClusters.settings.clusterClusterMarkersUrl not set, can show cluttered markers!");
            }
            showClutterCluster({html:"<div style=\"height: 100px;\"><h2>Op deze plek is meer dan één item aanwezig.</h2></div>"});
        }
        
        function showClutterCluster(data){
        
            var html = data.html;
        
            // create a semi marker to show an infoWindow
            var semiMarker = $.extend({}, UGMOMarker.prototype);
            semiMarker.markerLatLon = new google.maps.LatLng(cluster.lat, cluster.lon);
            semiMarker.ugmo_ = ugmo_;
            semiMarker.icon = {size:{width:ugmo_.UGMOShowClusters.settings.icon.width, height:ugmo_.UGMOShowClusters.settings.icon.height}};
            
            var data = {markerInfo:{html:html}};
            
            semiMarker.showMarkerInfo(data);        
        
        }
    }
    
    /* functions that construct the "markers" for the clusters */
  
    var ClusterMarker = function(map, location, div, anchor){   
        this.div_ = div;
        this.location_ = location;
        this.map_ = map;
        this.anchor_ = anchor;
        
        this.setMap(map);
    };
    
    ClusterMarker.prototype = new google.maps.OverlayView();
		
    ClusterMarker.prototype.onAdd = function() {
        var pane = this.getPanes().overlayMouseTarget;
        pane.appendChild(this.div_);
    }

    ClusterMarker.prototype.onRemove = function() {
        this.div_.parentNode.removeChild(this.div_);
    }

    ClusterMarker.prototype.draw = function() {
        
        var projection = this.getProjection();
        var position = projection.fromLatLngToDivPixel(this.location_);
    
        this.div_.style.position = 'absolute';
        this.div_.style.left = (position.x - this.anchor_[0])+ 'px';
        this.div_.style.top = (position.y - this.anchor_[1]) + 'px';
        this.div_.style.display = 'block';
        this.div_.style.zIndex = 3000;
    };
    
}());
