$.extend(($.plazes = $.plazes || {}), {

    map: (function() {

      var ICON_PATH = '/images/pins/single/', BASE_ICON;

      //console.log('');

      return function(container, plazes, options) {

          if (!(typeof GBrowserIsCompatible == 'function'
                && GBrowserIsCompatible())) return;

          options = options || {};
          options.debug = options.debug || false;

          BASE_ICON = new GIcon();
          BASE_ICON.iconSize = new GSize(36, 46);
          BASE_ICON.iconAnchor = new GPoint(0, 46);

          var self = this;

          var PlazesInfoWindow = function(marker, html, type){
              this.marker = marker;
              this.html = html;
              this.type = type || 'small';
          };
          PlazesInfoWindow.prototype = new GOverlay();
          PlazesInfoWindow.prototype.initialize = function(map) {

              /*var p = this.marker.getPoint(), c = map.getCenter();

              // bounds inside of which a point has to be so that an
              // info window is fully visible...
              var b = new GLatLngBounds(
                  map.fromContainerPixelToLatLng(new GPoint(50, map.getSize().height - 20)),
                  map.fromContainerPixelToLatLng(new GPoint(map.getSize().width - 190, 150))
              );

              if (!b.contains(p)) {
                  // pan map so that info window is in the viewport
                  map.panTo(
                      new GLatLng(
                          p.lat() > b.getNorthEast().lat() ?
                              c.lat() + (p.lat() - b.getNorthEast().lat()) :
                              p.lat() < b.getSouthWest().lat() ?
                                  c.lat() + (p.lat() - b.getSouthWest().lat()) :
                                  c.lat(),
                          p.lng() > b.getNorthEast().lng() ?
                              c.lng() + (p.lng() - b.getNorthEast().lng()) :
                              p.lng() < b.getSouthWest().lng() ?
                                  c.lng() + (p.lng() - b.getSouthWest().lng()) :
                                  c.lng()
                      )
                  );
              }*/

              var p = map.fromLatLngToContainerPixel(this.marker.getPoint());

              // bounds inside of which a point has to be so that an
              // info window is fully visible...
              var minX = 50, maxX = map.getSize().width - 190,
                  minY = 135, maxY = map.getSize().height - 20;

              var b = new GBounds([new GPoint(minX, minY), new GPoint(maxX, maxY)]);

              if (!b.containsPoint(p)) {
                  // pan map so that info window is in the viewport
                  map.panBy(
                      new GSize(
                          p.x > maxX ? -(p.x - maxX) :
                              p.x < minX ? -(p.x - minX) :
                                  0,
                          p.y > maxY ? -(p.y - maxY) :
                              p.y < minY ? -(p.y -minY) :
                                  0
                      )
                  );
              }

              /*if (options.debug) {
                  var nw = new GLatLng(b.getNorthEast().lat(), b.getSouthWest().lng());
                  var ne = b.getNorthEast();
                  var se = new GLatLng(b.getSouthWest().lat(), b.getNorthEast().lng());
                  var sw = b.getSouthWest();
                  map.addOverlay(new GPolyline([sw, se, ne, nw, sw], '#ff0000', 1, 1));
              }*/

              this.container = $('<div id="plazes-info-window" class="' + this.type + '"></div>')[0];
              this.yield = $('<div class="yield"></div>').appendTo(this.container)[0];
              this.yield.innerHTML = this.html;

              map.getPane(G_MAP_FLOAT_PANE).appendChild(this.container);

              $.data(this.container, 'info_window', this);

              this.position();

              var a = document.createElement('a');
              a.className = 'remove';
              a.href = '#';
              a.title = _('Close');
              a.innerHTML = '[x]';
              this.container.appendChild(a);
              a.onclick = closePlazesInfoWindow;
          };
          PlazesInfoWindow.prototype.remove = function(){
              this.container.parentNode.removeChild(this.container);
          };
          PlazesInfoWindow.prototype.redraw = function(){
              // no need for this yet
          };
          PlazesInfoWindow.prototype.position = function() {
              var fromLatLngToDivPixel = map.fromLatLngToDivPixel(this.marker.getPoint());
              this.container.style.top = fromLatLngToDivPixel.y - this.container.offsetHeight + 'px';
              this.container.style.left = fromLatLngToDivPixel.x + 'px';
          }

          var closePlazesInfoWindow = function() {
              var $container = $('div', map.getPane(G_MAP_FLOAT_PANE));
              if ($container.length) {
                  $container.data('info_window').marker.show();
                  $container.remove();
              }
              return false;
          };

          var map = new GMap2( $(container)[0] );
          map.setCenter(new GLatLng(0, 0), 2);
          map.enableDoubleClickZoom();

          [
            new GLargeMapControl(),
            new GMapTypeControl(),
            new GOverviewMapControl(),
            new GScaleControl()
          ].forEach(function(control) {
              map.addControl(control);
          });

          // Keep UI responsive by splitting up rendering of markers
          // into smaller chunks...
          (function() {

              // initialize
              var slice = 0;

              (function() {
                  plazes.slice(slice, (slice += 50)).forEach(function(plaze) {
                      var marker = new GMarker(new GLatLng(plaze.latitude, plaze.longitude), {
                          icon: new GIcon(BASE_ICON, ICON_PATH + plaze.category + '.png'),
                          title: plaze.name
                      });
                      map.addOverlay(marker);

                      GEvent.addListener(marker, 'click', function() {
                          if (typeof PlazesInfoWindow != 'undefined') {
                                closePlazesInfoWindow(); // close other
                                this.hide();
                                map.addOverlay(new PlazesInfoWindow(this, plaze.info));
                          }
                          // fallback...
                          else
                              marker.openInfoWindowHtml(plaze.info);
                      });

                  });

                  if (slice < plazes.length)
                      setTimeout(arguments.callee, 0);

              })();

          })();

          GEvent.addListener(map, 'zoomend', function() {
              var $container = $('div', map.getPane(G_MAP_FLOAT_PANE));
              if ($container.length)
                  $container.data('info_window').position();

          });

          GEvent.addDomListener(window, 'unload', GUnload); // prevent memory leakage

      };

    })()

});

