/**
 * Basic jquery implementation for google maps with some handy features
 * @author Mike De Smet <mike@ratus.nl>
 */
(function($){ 
    // too lazy to type google.maps all the time
    var gm = google.maps; 
    var map;
    var settings = $.extend({}, {
		options: {
		},
        imagePath: '/img/externals/maps/',
		latitude: 52.1326330,
		longitude: 5.291265999999999,
		mapTypeId: gm.MapTypeId.ROADMAP, // HYBRID, ROADMAP, TERRAIN, SATELITE
		bounds: [
			[53.67560, 7.227140500000001], // NE of Netherlands
			[50.75038370, 3.33160] // SW of Netherlands
		],
        restrainToBounds: true,
      //  disableDefaultUI: true,
        mapTypeControl: false,
        panControl: false,
        rotateControl: false,
        streetViewControl: false,
        overviewMapControl: false,
		key: '',
		zoom: 8, 
		map: undefined,
      //  draggable: false,
        maxZoom: 11,
        minZoom: 7,
        randomDrop: false,
        getMarkersUrl: document.location + '/',
        getMarkersRequestType: 'json',
        getMarkersExtraParams: {},
        styles: undefined,
        callbacks: {
            getMarkerOptions: undefined,
            addMarkers: undefined
        },
        markerMouseOverZoom: 3,
        markerMouseOutZoom: 4,
        markerClass: google.maps.Marker,
        reCenterOnMinZoom: false,
        markerClusterer: {
            gridSize: 30,
            styles: [{
                url: 'people35.png',
                height: 35,
                width: 35,
                anchor: [16, 0],
                textColor: '#ff00ff',
                textSize: 10
              }, {
                url: 'people45.png',
                height: 45,
                width: 45,
                anchor: [24, 0],
                textColor: '#ff0000',
                textSize: 11
              }, {
                url: 'people55.png',
                height: 55,
                width: 55,
                anchor: [32, 0],
                textColor: '#ffffff',
                textSize: 12
              }]
        },
        
        icon: { // default green small icon settings:
            image: function (marker, scale) { 
                return new google.maps.MarkerImage(
                    settings.imagePath + 'mm_20_green.png',
                    new google.maps.Size(12,20),
                    new google.maps.Point(0,0),
                    new google.maps.Point(12,20)
                );
            },
            shadow: function (marker, scale) {
                return new google.maps.MarkerImage(
                    settings.imagePath + 'mm_20_shadow.png',
                    new google.maps.Size(26,20),
                    new google.maps.Point(0,0),
                    new google.maps.Point(12,20)
                );
            },
            shape: function (marker, scale) { 
                return {
                    coords: [8,0,10,1,10,2,11,3,11,4,11,5,11,6,11,7,11,8,10,9,9,10,9,11,8,12,8,13,7,14,7,15,7,16,6,17,6,18,6,19,5,19,5,18,5,17,4,16,4,15,4,14,4,13,3,12,3,11,2,10,1,9,0,8,0,7,0,6,0,5,0,4,0,3,1,2,1,1,3,0,8,0],
                    type: 'poly'
                };
            }
        }
    });

    var callbacks = {
        getMarkerOptions: function (marker) {
            
            var latLng = new gm.LatLng(parseFloat(marker['lat'],10), parseFloat(marker['long'], 10));

            var options = {
                data: marker,
                position: latLng
            };
            
            if (!settings.markerClusterer)
            {
                options.map = map;

                if (settings.randomDrop)
                    options.animation = gm.Animation.DROP;
            }
            
            options.icon = settings.icon.image(options, settings.markerMouseOutZoom || 1);
            options.shadow = settings.icon.shadow(options, settings.markerMouseOutZoom || 1);
            options.shape = settings.icon.shape(options, settings.markerMouseOutZoom || 1);
             
            return options;
        },
        
        /**
         * Called after http request
         */
        addMarkers: function (data, XMLHttpRequest, ajaxOptions) {
        //    console.log(this);
            
            var $this = $(this);
            var markers = [];
            var settings = $this.googlemap('settings');
            if (!$.isArray(data))
				data = $.parseJSON(data);
				
            if (!$.isArray(data))
            {
                $.error('Invalid data gotten');
                return;
            }

            for (var i = 0, dataLength = data.length; i < dataLength; i++) 
            {
                var markerOptions = $.proxy(callbacks.getMarkerOptions, this)(data[i]);
                if (settings.markerClusterer || !settings.randomDrop)
                    markerOptions = new settings.markerClass(markerOptions);
                    // markerOptions = new gm.Marker(markerOptions);
                
                markers.push(markerOptions);
            }
            
            if (settings.randomDrop && !settings.markerClusterer)
            {
                var markerIndex = 0;
                for (var i = 0, len = markers.length; i < len; i++)
                {
                    setTimeout( function () {
                        var marker = new settings.markerClass(markers[markerIndex]);
                        // var marker = new gm.Marker(markers[markerIndex]);
                        $this.trigger('addMarker.googlemap', marker);
                        markerIndex++;
                    }, 200 * i);
                }
            }
            else
            {
                for (var i = 0, len = markers.length; i < len; i++)
                {
                    $this.trigger('addMarker.googlemap', markers[i]);
                }
                
                if (settings.markerClusterer)
                {
                    if (settings.maxZoom)
                        settings.markerClusterer.maxZoom = settings.maxZoom - 1;
                    settings.markerClusterer = new MarkerClusterer(settings.map, markers, settings.markerClusterer);
                }
            }
            
            return $this;
        }
    };
    
    var events = {
        init: function (event) {
            
		    var latlng = new gm.LatLng(settings.latitude, settings.longitude);
		    var options = $.extend({center: latlng}, settings);
    //        var settings = this.methods.settings();
            // replace original callbacks
            $.each(settings.callbacks, function (key, val) {
                if (!$.isFunction(val)) 
                    return;
                callbacks[key] = val;
            });
		    
		    map = new gm.Map(this, options);
            settings.map = map;
            
            gm.event.addListener(map, 'bounds_changed', $.proxy(events.boundsChanged, this));
		    if (settings.bounds)
		    {
		    	var bounds = new gm.LatLngBounds();
		    	$.each(settings.bounds, function(idx, bound) {
                    if (bound === undefined)
                        return;
                    
		    		bounds.extend(new gm.LatLng(bound[0], bound[1]));
		    	});
                
		    	map.fitBounds(bounds);
                                
                settings.googleBounds = bounds; // save the google bounds
                
                if (settings.getMarkersUrl)
                {
					var opts = {bounds: methods.serializeBounds(settings.bounds)};
					if (typeof settings.getMarkersExtraParams == 'object')
						opts = $.extend({}, opts, settings.getMarkersExtraParams);
					$.ajax(
                        settings.getMarkersUrl, 
                        {
                            data: opts, 
                            success: $.proxy(callbacks.addMarkers, this),
                            dataType: 'jsonp'
                        }
                        );
                }
		    }
            
            gm.event.addListener(map, 'zoom_changed', $.proxy(events.zoomChanged, this));
            gm.event.trigger(settings.map, "resize");
            
            if (settings.markerClusterer && settings.markerClusterer.styles)
            {
                // change to correct imagePath
                $.each(settings.markerClusterer.styles, function (idx, val) {
                    val.url = settings.imagePath + val.url;
                });
            }
        },
        destroy: function (event) {

        },
        zoomChanged: function () {
            var settings = $(this).googlemap('settings');
            google.maps.event.trigger(settings.map, "resize");
            if (!settings.reCenterOnMinZoom)
                return;
                
            if (typeof settings.googleBounds == 'object' && settings.map.getZoom() == settings.minZoom)
                settings.map.setCenter(settings.googleBounds.getCenter());
            
        }, 
        boundsChanged: function () {
            var settings = $(this).googlemap('settings');
            if (settings.restrainToBounds)
                _restrainToBounds(settings.map, settings.googleBounds);
        },
        addMarker: function (event, marker) {
            var settings = $(this).googlemap('settings');
            if (settings.markerMouseOverZoom)
            {
                google.maps.event.addListener(marker, 'mouseover', function (event) {
                    
                    if (this.open) return;
                    
                    this.setIcon(settings.icon.image(marker, settings.markerMouseOverZoom || 1));
                    this.setShadow(settings.icon.shadow(marker, settings.markerMouseOverZoom || 1));
                    this.setShape(settings.icon.shape(marker, settings.markerMouseOverZoom || 1));
                    
                    this.getDOMElement().css({border: 'red solid 5px'});
                    this.open = true;
                }); 
                
                google.maps.event.addListener(marker, 'mouseout', function (event) {
                    
                    if (!this.open) return;
                    
                    this.setIcon(settings.icon.image(marker, settings.markerMouseOutZoom));
                    this.setShadow(settings.icon.shadow(marker, settings.markerMouseOutZoom));
                    this.setShape(settings.icon.shape(marker, settings.markerMouseOutZoom));
                    this.open = false;
                });
            }
        }
    };
    
    var methods = {
        init: function (options) { 
        	
            return this.each(function(){
                var $this = $(this),
                    data = $this.data('googlemap');
                    
                options = $.extend({}, settings, typeof options === 'object' ? options : {});
                settings = options;
                 $this.data('googlemap', {initialized: true});

                $this.data('googlemap.settings', options);
                $.each(events, function (eventName, handler) {
                    var fullEventName = eventName + '.googlemap';
                    $this.bind(fullEventName, handler);
                    
                    if (typeof options[eventName] === 'function')
                    {
                        $this.bind(fullEventName, options[eventName]);
                    }
                });
                
                $this.trigger('init.googlemap');
            });
        },
        destroy: function () {
            return this.each(function(){
                var $this = $(this);
                var options = methods.settings();
                
                $this.trigger('destroy.googlemap');
                $.each(events, function (eventName, handler) {
                    $this.unbind(eventName + '.googlemap');
                });
                
                $this.removeData('googlemap');
                $this.removeData('googlemap.settings');
            });
        },
        serializeBounds: function (bounds) {
            return bounds[0][0] + ',' + bounds[0][1] + '|' + bounds[1][0] + ',' + bounds[1][1];
        },
        getMap: function () {
            return this.each(function(){
                return settings.map;
            });
        },
        data: function (data) { 
            methods.setting('data', data);
        },
        settings: function (settings) {
            if (settings === undefined)
                return $(this).data('googlemap.settings');
            
            return this.each(function(){
                var orig = $(this).data('googlemap.settings');
                if (orig === undefined) orig = {};
                orig = $.extend(orig, settings);
                $(this).data('googlemap.settings', orig);
            });
        },
        setting: function (setting, value) {
            if (value === undefined)
            {
                var data = $(this).data('googlemap.settings');
                return data[setting];
            }
            
            return this.each(function(){
                var orig = $(this).data('googlemap.settings');
                if (orig === undefined) orig = {};
                orig[setting] = value;
                $(this).data('googlemap.settings', orig);
            });
        },
        _eoo: false
    };
    
    function _restrainToBounds(map, bounds)
    {
        var c = map.getCenter();
        if (bounds.contains(c))
            return;
        
        var x = c.lng(),
            y = c.lat(),
            maxX = bounds.getNorthEast().lng(),
            maxY = bounds.getNorthEast().lat(),
            minX = bounds.getSouthWest().lng(),
            minY = bounds.getSouthWest().lat();

        if (x < minX) x = minX;
        if (x > maxX) x = maxX;
        if (y < minY) y = minY;
        if (y > maxY) y = maxY;

        map.setCenter(new gm.LatLng(y, x));
    }
    
    $.fn.googlemap = (function(method){
        if (methods[method])
            return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
        else if (typeof method === 'object' || !method)
            return methods.init.apply(this, arguments);
        
        $.error( 'Method ' +  method + ' does not exist on jQuery.googlemap' );
        return undefined;
    });
    
    if ($.log === undefined)
    {
    	$.log = function () {if (typeof console != "undefined" && typeof console.log != "undefined") console.log(arguments);};
    }
})(typeof jQueryForExternals === 'undefined' ? jQuery : jQueryForExternals);

