function SmartInfoWindow(opts) {
  google.maps.OverlayView.call(this);
  this.latlng_ = opts.position;
  this.content_ = opts.content;
  this.hasPhoto_ = opts.hasPhoto;
  this.map_ = opts.map;
  this.height_ = 110;
  this.width_ = 320;
  this.size_ = new google.maps.Size(this.height_, this.width_);
  this.offsetVertical_ = -this.height_;
  this.offsetHorizontal_ = 0;
  this.panned_ = false;
  this.shadowDiv_ = null;
  this.setMap(this.map_);

  var me = this;

  google.maps.event.addListener(this.map_, 'bounds_changed', function() {
    me.draw();
  });
};

SmartInfoWindow.prototype = new google.maps.OverlayView();

SmartInfoWindow.prototype.onRemove = function() {
  if (this.div_) {
    this.div_.parentNode.removeChild(this.div_);
    this.div_ = null;

    if( this.shadowDiv_ ) {
       this.shadowDiv_.parentNode.removeChild(this.shadowDiv_);
    }
  }
};

SmartInfoWindow.prototype.onAdd = function() {
  this.createElement();
};

SmartInfoWindow.prototype.draw = function() {
  if (!this.getProjection()) {
    return;
  }

  var pixPosition = this.getProjection().fromLatLngToDivPixel(this.latlng_);
  var centerPosition = this.getProjection().fromLatLngToDivPixel(this.map_.getCenter());
  var centerPositionReal = new google.maps.Point(this.map_.getDiv().offsetWidth/2, this.map_.getDiv().offsetHeight/2);
  var centerOffsetX = -centerPosition.x + centerPositionReal.x;
  var centerOffsetY = -centerPosition.y + centerPositionReal.y;

  if (!pixPosition) return;
  var alignment = this.getBestAlignment();
  var paddingTop = 0;
  var paddingLeft = 0;
  var widthLess = 0;

  this.offsetX_ = 25;
  this.offsetY_ = -40;
  this.div_.className = 'google_cloud';
  this.div_.style.left = (pixPosition.x + this.offsetX_) + centerOffsetX + 'px';
  this.div_.style.top = (pixPosition.y + this.offsetY_) + centerOffsetY + 'px';
  this.div_.style.display = 'block';

  this.shadowDiv_.style.height = $(this.div_).innerHeight();
  this.shadowDiv_.style.display = 'block';
  this.shadowDiv_.style.top = (pixPosition.y + this.offsetY_) + centerOffsetY + 7 + 'px';
  this.shadowDiv_.style.left = (pixPosition.x + this.offsetX_) + centerOffsetX + 15 + 'px';

  if( !this.panned_ ) {
    this.panned_ = true;
    this.maybePanMap();
  }
};

SmartInfoWindow.prototype.createElement = function() {
  var panes = this.getPanes();
  var div = this.div_;
  if (!div) {
    div = this.div_ = document.createElement('div');
    div.className = 'google_cloud';
    div.style.position = 'absolute';
    var wrapperDiv = this.wrapperDiv_ = document.createElement('div');
    var contentDiv = document.createElement('div');
    if (typeof this.content_ == 'string') {
      contentDiv.innerHTML = this.content_;
    } else {
      contentDiv.appendChild(this.content_);
    }

    var topDiv = document.createElement('div');
    topDiv.style.textAlign = 'right';
    topDiv.style.marginBottom = '-12px';
    topDiv.style.width = '320px';

    var closeImg = document.createElement('img');
    closeImg.src = '/skins/destinationmap/images/close_gray.png';
    closeImg.alt = '/skins/destinationmap/images/close_gray.png';
    closeImg.low = '/skins/destinationmap/images/close_hover.png';
    closeImg.style.width = '16px';
    closeImg.style.height = '16px';
    closeImg.style.cursor = 'pointer';
    topDiv.appendChild(closeImg);

    function removeSmartInfoWindow(ib) {
        return function() {
            ib.setMap(null);
        };
    };

    $(closeImg).hover(function() { this.src = $(this).attr('low'); }, function() { this.src = $(this).attr('alt'); });

    google.maps.event.addDomListener(closeImg, 'click', removeSmartInfoWindow(this));

    var arrowDiv = document.createElement('div');
    arrowDiv.className = 'google_cloud_arrow';

    wrapperDiv.appendChild(topDiv);
    wrapperDiv.appendChild(contentDiv);
    wrapperDiv.appendChild(arrowDiv);
    div.appendChild(wrapperDiv);
    div.style.display = 'none';
    $(this.map_.getDiv()).append(div);

    var shadowDiv = this.shadowDiv_ = document.createElement('div');
    shadowDiv.className = 'google_cloud_shadow';
    $(this.map_.getDiv()).append(shadowDiv);

  } else if (div.parentNode != panes.floatPane) {
    div.parentNode.removeChild(div);
    panes.floatPane.appendChild(div);
  } else {
  }
};

SmartInfoWindow.mouseFilter = function(e) {
  e.returnValue = 'true';
  e['handled'] = true;
};

SmartInfoWindow.prototype.close = function() {
  this.setMap(null);
};

SmartInfoWindow.prototype.maybePanMap = function() {
  var map = this.map_;
  var projection = this.getProjection();
  var bounds = map.getBounds();
  if (!bounds) return;

  var iwWidth = this.width_+20;
  var iwHeight = $(this.div_).innerHeight()+10;
  var iwOffsetX = this.offsetX_;
  var iwOffsetY = this.offsetY_;
  var anchorPixel = projection.fromLatLngToDivPixel(this.latlng_);
  var bl = new google.maps.Point(anchorPixel.x + iwOffsetX + 20, anchorPixel.y + iwOffsetY + iwHeight);
  var tr = new google.maps.Point(anchorPixel.x + iwOffsetX + iwWidth, anchorPixel.y + iwOffsetY);
  var sw = projection.fromDivPixelToLatLng(bl);
  var ne = projection.fromDivPixelToLatLng(tr);

  if (!map.getBounds().contains(ne) || !map.getBounds().contains(sw)) {
    map.panToBounds(new google.maps.LatLngBounds(sw, ne));
  }
};

SmartInfoWindow.Align = {
  ABOVE: 0,
  LEFT: 1,
  RIGHT: 2,
  BELOW: 3
};

SmartInfoWindow.prototype.getBestAlignment = function() {
  var bestAlignment = SmartInfoWindow.Align.LEFT;
  var minPan = 0;

  for (var alignment in SmartInfoWindow.Align) {
    var alignment = SmartInfoWindow.Align[alignment];
    var panValue = this.getPanValue(alignment);
    if (panValue > minPan) {
      minPan = panValue;
      bestAlignment = alignment;
    }
  }

  return bestAlignment;
};

SmartInfoWindow.prototype.getPanValue = function(alignment) {
  var mapSize = new google.maps.Size(this.map_.getDiv().offsetWidth,
      this.map_.getDiv().offsetHeight);
  var bounds = this.map_.getBounds();
  var sideLatLng;
  switch (alignment) {
    case SmartInfoWindow.Align.ABOVE:
      sideLatLng = new google.maps.LatLng(bounds.getNorthEast().lat(), this.latlng_.lng());
      break;
    case SmartInfoWindow.Align.BELOW:
      sideLatLng = new google.maps.LatLng(bounds.getSouthWest().lat(), this.latlng_.lng());
      break;
    case SmartInfoWindow.Align.RIGHT:
      sideLatLng = new google.maps.LatLng(this.latlng_.lat(), bounds.getNorthEast().lng());
      break;
    case SmartInfoWindow.Align.LEFT:
      sideLatLng = new google.maps.LatLng(this.latlng_.lat(), bounds.getSouthWest().lng());
      break;
  }
  var dist = SmartInfoWindow.distHaversine(this.latlng_.lat(), this.latlng_.lng(), sideLatLng.lat(), sideLatLng.lng());
  return dist;
};

SmartInfoWindow.toRad = function(num) {
    return num * Math.PI / 180;
};

SmartInfoWindow.distHaversine = function(lat1, lon1, lat2, lon2) {
  var R = 6371; // earth's mean radius in km
  var dLat = SmartInfoWindow.toRad(lat2 - lat1);
  var dLon = SmartInfoWindow.toRad(lon2 - lon1);
  lat1 = SmartInfoWindow.toRad(lat1), lat2 = SmartInfoWindow.toRad(lat2);

  var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
          Math.cos(lat1) * Math.cos(lat2) *
          Math.sin(dLon / 2) * Math.sin(dLon / 2);
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  var d = R * c;
  return d;
};

