/*
 * jQuery UI MarkerMap 0.1
 *
 * Author: Jannon Frank
 * 
 * Depends:
 *  jquery.ui.core.js
 *  jquery.ui.widget.js
 *  Google maps JavaScript API V3
 * markerclusterer.js
 * 
 * Marker arrays are expected to be of the following format:
 *  [type:string, id:mixed, lat:float, lng:float, info:string]
 * 
 * type: the type of marker.  Options in markerOptions will be looked up based on this type
 * lat: the latitude of the marker
 * lng: the longitude of the marker
 * info: the info to be displayed when the marker is clicked
 */

//TODO: support more marker events (mouseover, dblclick, etc.)
//TODO: support multiple infoWindows

(function($) {
    var TYPE = 0,
        ID = 1,
        LAT = 2,
        LNG = 3,
        INFO = 4,
        defaultMarkerManager = {
        create: function() {
            $.noop();
        },
        addMarkers: function(markers) {
            var type, opts, m;
            
            for( var i = 0, l = markers.length; i < l; i++) {
                type = markers[i][TYPE];
                opts = this.options.markerOptions[type];
                if (!opts) { opts = {}; }
                m = new google.maps.Marker($.extend({}, opts, {
                    position: new google.maps.LatLng(markers[i][LAT], markers[i][LNG]),
                    map: this.map
                }));
            }
        },
        clearMarkers: function() {
            for( var i = 0, l = this.markers.length; i < l; i++) {
                google.maps.clearListeners(this.markers[i], "click");
                this.options.markers[i].setMap(null);
            }
        },
        destroy: function() {
            $.noop();
        },
        showMarker: function(marker) {
            //marker.setMap(this.map);
            marker.setVisible(true);
        },
        hideMakrer: function(marker) {
            //marker.setMap(null);
            marker.setVisible(false);
        }
    };
    
    $.widget("ui.markermap", {
        options: {
            markers: [],
            manager: 'default',
            managers: {
                'default': defaultMarkerManager
            },
            mapOptions: {
                streetViewControl: false,
                mapTypeId: google.maps.MapTypeId.ROADMAP
            },
            markerOptions: {},
            singleInfoWindow: true,
            infoWindow: {
                maxWidth: 500
            }
        },
        _create: function() {
            var self = this,
                o = this.options,
                el = this.element.addClass("ui-markermap"),
                ready = (this.ready = false);
                
            if ($.metadata) {
                $.extend(o.mapOptions, el.metadata());
            }
            this.map = new google.maps.Map(el[0], o.mapOptions);
            this.markers = [];
            this.markerDict = {};
            this.waiting = [];
            //this.infoWindow = new google.maps.InfoWindow(o.infoWindow);
            google.maps.event.addListenerOnce(this.map, "tilesloaded", function() {
                self.ready = true;
                self.addMarkers(o.markers);
                for(var i = 0, l = self.waiting.length; i < l; i++) {
                    self.waiting[i]();
                }
            });
            o.managers[o.manager].create.call(this);
            
        },
        _setOption: function(option, value) {
            if (option == "markers") {
                this.clearMarkers();
            }
            $.Widget.prototype._setOption.apply(this, arguments);
            
            if (option == "markers") {
                this.addMarkers(value);
            }
        },
        destroy: function() {
            $.Widget.prototype.destroy.apply(this, arguments);
            this.options.managers[this.options.manager].destroy.call(this);
            this.clearMarkers();
            this.markers = null;
            this.waiting.splice(0, this.waiting.length);
            this.markerDict = null;
            if (this.infoWindow) {
                this.infoWindow.setMap(null);
                this.infoWindow = null;    
            }
            //TODO: destroy map and event listeners
        },
        addMarkers: function(markerData) {
            var o = this.options,
                markers = [],
                marker,
                ids;
            
            for(var i = 0, l = markerData.length; i < l; i++) {
                marker = this._createMarker(markerData[i]);
                markers.push(marker);
                ids = markerData[i][ID].split(" ");
                for(var j = 0; j < ids.length; j++) {
                    this.markerDict[ids[j]] = {
                        marker: marker,
                        data: markerData[i],
                        ids: ids
                    };
                }
            } 
            o.managers[o.manager].addMarkers.call(this, markers);
            o.markers = o.markers.concat(markerData);
            this.markers.concat(markers);
        },
        clearMarkers: function() {
            var o = this.options;
            o.managers[o.manager].clearMarkers.call(this);
            o.markers.splice(0, o.markers.length);
            this.markers.splice(0, o.markers.length);
            for(var marker in this.markerDict) {
                if (this.markerDict.hasOwnProperty(marker)) {
                    delete this.markerDict[marker];
                }
            }
        },
        getMap: function() {
            return this.map;
        },
        showMarker: function(id) {
            var o = this.options,
                marker = this.markerDict[id];
            
            if (marker) {
                if(marker.data[ID].indexOf(" ") != -1) {
                    //this belongs to a multi marker so we need to handle differently
                    marker.ids.push(id);
                    if (marker.ids.length == 1) {
                        o.managers[o.manager].showMarker.call(this, marker.marker);
                    }
                } else {
                    o.managers[o.manager].showMarker.call(this, marker.marker);
                }
            }
        },
        hideMarker: function(id) {
            var o = this.options,
                marker = this.markerDict[id];
            if (marker) {
                if (marker.data[ID].indexOf(" ") != -1) {
                    //this belongs to a multi marker so we need to handle differently
                    marker.ids.splice($.inArray(id, marker.ids), 1);
                    if (marker.ids.length === 0) {
                        o.managers[o.manager].hideMarker.call(this, marker.marker);
                    }
                }
                o.managers[o.manager].hideMarker.call(this, marker.marker);
            }
        },
        onReady: function(callback) {
            if(!this.ready) {
                this.waiting.push(callback);
            } else {
                callback();
            }
        },
        _createMarker: function(data) {
            var options = this.options.markerOptions[data[TYPE]],
                latLng = new google.maps.LatLng(data[LAT], data[LNG]),
                m;
            if (!options) { options = {}; }
            m = new google.maps.Marker($.extend({}, options, {position: latLng, map: this.map}));
            this._bindEvents(m, data);
            return m;
        },
        _bindEvents: function(marker, data) {
            var self = this,
                o = this.options;
            google.maps.event.addListener(marker, 'click', function() {
                if (self.infoWindow) {
                    self.infoWindow.close();
                    self.infoWindow = null;
                }
                self.infoWindow = new google.maps.InfoWindow($.extend({}, o.infoWindow, {content: data[INFO]}));
                //self.infoWindow.setContent(data[INFO]); //TODO: support multiple infoWindows
                self.infoWindow.open(this.map, marker);
            });
        }   
    });
    
    $.ui.markermap.TYPE = TYPE;
    $.ui.markermap.ID = ID;
    $.ui.markermap.LAT = LAT;
    $.ui.markermap.LNG = LNG;
    $.ui.markermap.INFO = INFO;
})(jQuery);
