  
(function($) {
    function fireEvent(opts, fn, self, arg) {
        if ($.isFunction(fn)) {
            try {
                return fn.call(self, arg);
            } catch (error) {
                if (opts.debug) {
                    alert("Error calling googlemaps." + fn + ": " + error);
                } else {
                    throw error;
                }
                return false;
            }
        }
        return true;
    }

    var current = null;
    var capasArray = [];

    function TextualZoomControl() {
    }



    function Googlemap(root, conf) {
        // current instance
        var self = this;
        if (!current) {
            current = self;
        }

        // internal variables
        var map;
        var geo;
        var bounds;
        var markers;
        //var index = 0;

        // configuration (comments show default values)
        var latitude = conf.latitude;    // -35
        var longitude = conf.longitude;   // 150
        var zoom = conf.zoom;        // 4
        var controls = conf.controls;    // true
        var zoomMapControl = conf.zoomMapControl;
        var typesMapControl = conf.typesMapControl;
        var labels = conf.labels;      // true
        var html = conf.html;        // null
        var addresses = conf.addresses;   // null
        var debug = conf.debug;       // false
        var useDefaultIcons = conf.useDefaultIcons;
        var customIcons = conf.customIcons;
        var recenteronclic = conf.recenteronclic;
        var trafico = conf.trafico;
        var traficoOn = trafico; //estas variables sirven como "semaforo", pasa saber si tengo que poner o sacar la capa al hacer clic sobre el boton
        var tipoMapaPredeterminado = conf.tipoMapaPredeterminado;
        var capaWiki = conf.capaWiki;
        var capawikiOn = capaWiki;
        var capaWebCams = conf.capaWebCams;
        var capaWebCamsOn = capaWebCams;
        var capaPanoramio = conf.capaPanoramio;
        var capaPanoramioOn = capaPanoramio;

        // methods
        $.extend(self, {
            // plugin specific
            getVersion: function() { return [1, 0, 0]; },
            getRoot: function() { return root; },

            // google maps specific
            getMap: function() { return map; },
            getGeo: function() { return geo; },
            getAddresses: function() { return addresses; },
            getBounds: function() { return bounds; },
            //getIndex: function() { return index; },
            getMarkers: function() { return markers; },

            // api
            isBrowserCompatible: function() {
                if ($.isFunction(GBrowserIsCompatible))
                    return GBrowserIsCompatible();

                return false;
            },
            initialise: function() {
                self.trace("initialising: " + this);
                if (self.isBrowserCompatible()) {
                    map = map || new GMap2(document.getElementById($(root)[0].id));
                    geo = geo || new GClientGeocoder();
                    bounds = bounds || new GLatLngBounds();
                    markers = markers || new Array();

                    GEvent.addListener(map, "load", function() {
                        self.trace("google map loaded!");
                    });

                    // set the map center
                    map.setCenter(new GLatLng(latitude, longitude), zoom);
                    if (!map.scrollWheelZoomEnabled()) {
                        map.enableScrollWheelZoom();
                    }
                    //set type map
                    if (tipoMapaPredeterminado == "Callejero")
                        map.setMapType(G_NORMAL_MAP)
                    if (tipoMapaPredeterminado == "Satelite")
                        map.setMapType(G_SATELLITE_MAP)
                    if (tipoMapaPredeterminado == "Hibrido")
                        map.setMapType(G_HYBRID_MAP)

                    // mark addresses on the map
                    if (addresses) {
                        if (addresses.length > 0) {
                            var i = 0;
                            while (i < addresses.length) {
                                if (addresses[i].length == 1) {
                                    self.geocode(i++);
                                } else {
                                    var point = new GLatLng(addresses[i][0], addresses[i][1], true);

                                    // extend bounds
                                    bounds = bounds || new GLatLngBounds();
                                    bounds.extend(point); self.trace("bounds extended");

                                    // marker
                                    var marker = self.createMarker(i++, point);
                                    self.trace(marker); self.trace("marker created");

                                    // add marker to array and display
                                    markers[i] = marker;
                                    map.addOverlay(marker);

                                    // onMarkerLoaded
                                    if (fireEvent(conf, self.onMarkerLoaded, self, i) === false) {
                                        return self;
                                    }
                                }
                            }
                        }
                    }

                    // add controls
                    if (controls) {
                        if (zoomMapControl != "large")
                            map.addControl(new GSmallMapControl());
                        else
                            map.addControl(new GLargeMapControl());
                        if (typesMapControl)
                            map.addControl(new GMapTypeControl());
                    }

                    TextualZoomControl.prototype = new GControl();
                    //agrego capa trafico si es necesario
                    if (trafico) {
                        var LayerTrafico = new GTrafficOverlay();
                        map.addOverlay(LayerTrafico);
                    }
                    // add extra layers
                    if (capaWiki) {
                        var LayerWiki = new GLayer("org.wikipedia.ar");
                        map.addOverlay(LayerWiki);
                    }

                    if (capaWebCams) {
                        var LayerWebCams = new GLayer("com.google.webcams");
                        map.addOverlay(LayerWebCams);
                    }

                    if (capaPanoramio) {
                        var LayerPanoramio = new GLayer("com.panoramio.all");
                        map.addOverlay(LayerPanoramio);
                    }

                    if (controls) {
                        TextualZoomControl.prototype.initialize = function(map) {
                            var container = document.createElement("div");
                            this.setContainerStyle_(container);
                            if (trafico) {
                                var zoomTraficoDiv = document.createElement("div");
                                container.appendChild(zoomTraficoDiv);
                                this.setButtonStyle_(zoomTraficoDiv);
                                zoomTraficoDiv.appendChild(document.createTextNode("Trafico"));
                                GEvent.addDomListener(zoomTraficoDiv, "click", function() {
                                    if (traficoOn == true) {
                                        zoomTraficoDiv.style.fontWeight = "normal";
                                        zoomTraficoDiv.style.borderColor = "";
                                        map.removeOverlay(LayerTrafico);
                                        traficoOn = false;
                                    } else {
                                        zoomTraficoDiv.style.fontWeight = "600";
                                        map.addOverlay(LayerTrafico);
                                        traficoOn = true;
                                    }
                                });
                            }
                            if (capaWiki) {
                                var zoomInDiv = document.createElement("div");
                                this.setButtonStyle_(zoomInDiv);
                                container.appendChild(zoomInDiv);
                                zoomInDiv.appendChild(document.createTextNode("Wikipedia"));
                                GEvent.addDomListener(zoomInDiv, "click", function() {
                                    if (capawikiOn == true) {
                                        zoomInDiv.style.fontWeight = "normal";
                                        map.removeOverlay(LayerWiki);
                                        capawikiOn = false;
                                    } else {
                                        zoomInDiv.style.fontWeight = "600";
                                        map.addOverlay(LayerWiki);
                                        capawikiOn = true;
                                    }
                                });
                            }
                            if (capaWebCams) {
                                var zoomOutDiv = document.createElement("div");
                                this.setButtonStyle_(zoomOutDiv);
                                container.appendChild(zoomOutDiv);
                                zoomOutDiv.appendChild(document.createTextNode("Camaras"));
                                GEvent.addDomListener(zoomOutDiv, "click", function() {
                                    if (capaWebCamsOn == true) {
                                        zoomOutDiv.style.fontWeight = "normal";
                                        map.removeOverlay(LayerWebCams);
                                        capaWebCamsOn = false;
                                    } else {
                                        zoomOutDiv.style.fontWeight = "600";
                                        map.addOverlay(LayerWebCams);
                                        capaWebCamsOn = true;
                                    }
                                });
                            }
                            if (capaPanoramio) {
                                var zoomPanoramioDiv = document.createElement("div");
                                this.setButtonStyle_(zoomPanoramioDiv);
                                container.appendChild(zoomPanoramioDiv);
                                zoomPanoramioDiv.appendChild(document.createTextNode("Fotografias"));
                                GEvent.addDomListener(zoomPanoramioDiv, "click", function() {
                                    if (capaPanoramioOn == true) {
                                        zoomPanoramioDiv.style.fontWeight = "normal";
                                        map.removeOverlay(LayerPanoramio);
                                        capaPanoramioOn = false;
                                    } else {
                                        zoomPanoramioDiv.style.fontWeight = "600";
                                        map.addOverlay(LayerPanoramio);
                                        capaPanoramioOn = true;
                                    }
                                });
                            }
                            map.getContainer().appendChild(container);
                            return container;
                        }

                        // By default, the control will appear in the top left corner of the
                        // map with 7 pixels of padding.
                        TextualZoomControl.prototype.getDefaultPosition = function() {
                            return new GControlPosition(125, new GSize(5, 5));
                        }

                        // Sets the proper CSS for the given button element.
                        TextualZoomControl.prototype.setButtonStyle_ = function(button) {
                            button.style.textDecoration = "none";
                            button.style.color = "#000000";
                            button.style.backgroundColor = "white";
                            button.style.font = "small Arial";
                            button.style.fontSize = "12px";
                            button.style.fontWeight = "600";
                            button.style.border = "1px solid black";
                            //button.style.borderColor = "#345684 #6C9DDF #6C9DDF #000000";
                            button.style.paddingTop = "1px";
                            button.style.paddingBottom = "1px";
                            button.style.paddingLeft = "5px";
                            button.style.paddingRight = "5px";
                            button.style.marginBottom = "2px";
                            button.style.textAlign = "center";
                            button.style.width = "6em";
                            button.style.cursor = "pointer";
                        };

                        TextualZoomControl.prototype.setDivIntStyle_ = function(button) {
                            
                            button.style.border = "1px solid";
                        };

                        TextualZoomControl.prototype.setContainerStyle_ = function(button) {
                            button.style.marginLeft = "80px";
                        };

                        map.addControl(new TextualZoomControl());
                    }
                }
            },

            // geocode(index, address, html, anchor) :
            //     index:     (number) index of the marker (obsolete when label == false)
            //     address:   (string) human readable address to query
            //     html:      [array] what to display on marker's "click" event
            //     anchor:    [array] simulate marker's "click" event outside the map via a link
            geocode: function(index) {
                geo = (geo == null) ? new GClientGeocoder() : geo;
                if (addresses && index >= 0) {
                    self.trace("processing address: [" + addresses[index] + "] (" + index + ")");
                    markers = markers || new Array();

                    // safer way of geocoding - avoids G_GEO_TOO_MANY_QUERIES
                    geo.getLocations(addresses[index], function(response) {
                        var statuscode = response.Status.code;

                        if (statuscode == G_GEO_SUCCESS) {
                            // success!
                            self.trace(response.Placemark);
                            var point = new GLatLng(response.Placemark[0].Point.coordinates[1], response.Placemark[0].Point.coordinates[0], true);

                            // extend bounds
                            bounds = bounds || new GLatLngBounds();
                            bounds.extend(point); self.trace("bounds extended");

                            // marker
                            var marker = self.createMarker(index, point);
                            self.trace(marker); self.trace("marker created");

                            // evento del marker para centrar el mapa al hacer clic sobre la marca
                            if (recenteronclic) {
                                GEvent.addListener(marker, "click", function() {
                                    zoom = 15;
                                    map.setCenter(marker.getLatLng(), zoom);
                                });
                            }
                            // add marker to array and display
                            markers[index] = marker;
                            map.addOverlay(marker);

                            // onMarkerLoaded
                            if (fireEvent(conf, self.onMarkerLoaded, self, index) === false) {
                                return self;
                            }
                        } else {
                            if (statuscode == G_GEO_TOO_MANY_QUERIES) {
                                // retry again after a short while
                                var delay = 600;
                                self.trace("index " + index + " will begin retry in " + delay + "ms")
                                setTimeout(function() {
                                    self.geocode(index);
                                }, delay);
                            } else {
                                self.trace("unknown error code: " + statuscode);
                                marker[index] = null;
                            }
                        }
                    });
                }
            },

            // onMarkerLoaded(index)
            //     internal function : DO NOT MODIFY
            onMarkerLoaded: function(index) {
                // set map bounds and zoom level to optimal level so all marker can fit
                //return self.optimiseZoomLevel();
            },

            // optimiseZoomLevel()
            optimiseZoomLevel: function(index) {
                if (bounds && (addresses.length == markers.length)) {
                    zoom = map.getBoundsZoomLevel(bounds);
                    map.setZoom(zoom);
                    map.setCenter(bounds.getCenter());
                }

                return true;
            },

            // createMarker(index, point)
            //     index:    (number) index of the marker (also used to generate a letter)
            //     point:    (GLatLng) latitude and longitude of the marker
            createMarker: function(index, point) {
                // create a base icon for all of our markers that specifies the
                // shadow, icon dimensions, etc.
                var baseIcon = new GIcon(G_DEFAULT_ICON);
                if (useDefaultIcons) {
                    baseIcon
                    baseIcon.shadow = "http://www.google.com/mapfiles/shadow50.png";
                    baseIcon.iconSize = new GSize(20, 34);
                    baseIcon.shadowSize = new GSize(37, 34);
                    baseIcon.iconAnchor = new GPoint(9, 34);
                    baseIcon.infoWindowAnchor = new GPoint(9, 2);
                } else {
                    baseIcon.shadow = customIcons.shadow;
                    baseIcon.iconSize = customIcons.iconSize;
                    baseIcon.shadowSize = customIcons.shadowSize;
                    baseIcon.iconAnchor = customIcons.iconAnchor;
                    baseIcon.infoWindowAnchor = customIcons.infoWindowAnchor;
                }
                var range = "Z".charCodeAt(0) - "A".charCodeAt(0) + 1;
                var letter = String.fromCharCode("A".charCodeAt(0) + (index % range));
                var letteredIcon = new GIcon(baseIcon);
                if (useDefaultIcons)
                    letteredIcon.image = "http://www.google.com/mapfiles/marker" + letter + ".png";
                else
                    letteredIcon.image = customIcons.image;

                var markerOptions = {
                    icon: letteredIcon,
                    bouncy: true
                };

                var marker = (labels) ? new GMarker(point, markerOptions) : new GMarker(point);
                if (html != undefined)
                    GEvent.addListener(marker, "click", function() {
                        marker.openInfoWindowHtml(html[index]);
                    });
                return marker;
            },

            // trace(arg, [args...]) : print everything in the arguments array
            trace: function() {
                if (!debug) return;

                var caller = arguments.caller || "self";
                for (i = 0; i < arguments.length; i++) {
                    var argument = arguments[i]; // print object as it is
                    var line = argument;
                    try {
                        // Firefox, Safari, Opera
                        console.debug(line);
                    } catch (error) {
                        // fails gracefully on IE, Chrome
                        alert(line);
                    }
                }
            }
        });

        function load() {
            self.initialise();
            return self;
        }

        load();
    }


    // jQuery plugin implementation
    jQuery.prototype.googlemap = function(conf) {
        // already constructed --> return API
        var api = this.eq(typeof conf == 'number' ? conf : 0).data("googlemap");
        if (api) { return api; }

        var opts = {
            latitude: -23,
            longitude: 133,
            zoom: 4,
            labels: true,
            controls: true,
            html: null,
            anchor: null,
            addresses: null,
            debug: false
        };

        $.extend(opts, conf);

        this.each(function() {
            var el = new Googlemap($(this), opts);
            $(this).data("googlemap", el);
        });

        return this;
    };
})(jQuery);


