/*
Copyright 2005-2009 James Tolley - http://www.bitperfect.com | http://www.gmaptools.com
All rights reserved.
This software is released under the LGPL. (http://www.opensource.org/licenses/lgpl-license.php)

  BpLabel - label overlay for Google Maps

These are the methods that should be implemented in this class:
("!" means that it's tested)
! get/setPane - set this before addOverlay, cannot be changed
! getMap/isMapped
! show/hide/isVisible
! get/setPoint === get/setLatLng
! getId
! get/setUserData
! getDiv
! copy
! remove
! initialize
! redraw
! get/setCursor
! get/set/resetZIndex/setHighZIndex
! get/setOpacity
! get/setClassName
! maintain event listeners from one addOverlay to another: maintain the same div
! able to hide initially; maintains hidden quality from one add to the next
! getSize/Height/Width
! get/setAnchor
! en/disableDragging/draggingEnabled/getDraggableObject/dragging

TODO:
  setLatLng() before addOverlay === death
    I think this is fixed
  openInfoWindow methods

*/

(function() {

var id = 0;

function BpLabel(latlng, html, hide, anchor) {
  GOverlay.apply(this, arguments);

  this._latlng  = latlng  || new GLatLng(0,0);
  this._html    = html    || '';
  this._hide    = hide    || false;
  this._anchor  = anchor  || 'center';

  this._id = ++id;

  this._pane = G_MAP_MARKER_PANE;
  this._cursor = false;
  this._opacity = 80;
  this._userData = {};
  this._className = false;
  this._stale_size = true; // we don't know our size yet
}

BpLabel.prototype = new GOverlay(new GLatLng(0,0));

BpLabel.prototype.getAnchor = function() {
  return this._anchor;
};

BpLabel.prototype.setAnchor = function(anchor) {
  if ((anchor == 'center' || anchor == 'nw') && this._anchor != anchor) {
    this._anchor = anchor;
    this.redraw();
  }
};

// returns only if it's mapped or was mapped and has not changed html or className since.
BpLabel.prototype.getSize = function() {
  if (!this._stale_size) {
    return this._size;
  }

  if (!this._div) {
    return false;
  }

  if(this.isVisible()) {
    var width = this._div.offsetWidth || this._div.style.pixelWidth || 0;
    var height = this._div.offsetHeight || this._div.style.pixelHeight || 0;
    this._size = new GSize(width, height);
    this._stale_size = false;
  }
  else if (this.isMapped()) {
    // put it off-map
    var top  = this._div.style.top;
    var left = this._div.style.left;
    this._div.style.top   = -screen.height + 'px';
    this._div.style.left  = -screen.width  + 'px';

    // show it
    this._div.style.display = '';

    // measure it,
    var width = this._div.offsetWidth || this._div.style.pixelWidth || 0;
    var height = this._div.offsetHeight || this._div.style.pixelHeight || 0;
    this._size = new GSize(width, height);
    this._stale_size = false;

    // hide it
    this._div.style.display = 'none';

    //move it back
    this._div.style.top   = top;
    this._div.style.left  = left;
  }
  // not yet mapped, or the html or className changed since it's been mapped, then return false
  if (this._stale_size) {
    return false;
  }

  return this._size;
};

BpLabel.prototype.getWidth = function() {
  var size = this.getSize();
  if (size) {
    return size.width;
  }
  return false;
};

BpLabel.prototype.getHeight = function() {
  var size = this.getSize();
  if (size) {
    return size.height;
  }
  return false;
};

BpLabel.prototype.setClassName = function(cName) {
  this._stale_size = true;

  this._className = cName;

  if (this._styled) {
    this.removeStyle();
  }

  if (this._div) {
    this._div.className = this._className;
    this._stale_size = true;
  }

  this.getSize();
};

BpLabel.prototype.getClassName = function() {
  return this._className;
};

BpLabel.defaultStyle = {
  fontSize: '14px',
  fontFamily: 'sans-serif',
  border: '1px solid black',
  backgroundColor: 'white',
  filter: 'alpha(opacity:80)',
  KHTMLOpacity: 0.8,
  MozOpacity: 0.8,
  opacity: 0.8,
  fontWeight: 'bold',
  whiteSpace: 'nowrap',
  paddingRight: '3px',
  paddingLeft: '3px'
};

BpLabel.prototype.addStyle = function() {
  if (this._div) {
    var style = BpLabel.defaultStyle;
    for (var key in style) {
      this._div.style[key] = style[key];
    }

    this._styled = true;
  }
};

BpLabel.prototype.removeStyle = function() {
  if (this._div) {
    var style = BpLabel.defaultStyle;
    for (var key in style) {
      this._div.style[key] = '';
    }

    this._styled = false;
  }
};

BpLabel.prototype.initialize = function(map) {
  this._map = map;

  // if we're doing this for the first time...
  if(!this._div) {
    this._div = document.createElement('div');
    this._div.style.position = 'absolute';

    if (this._className) {
      this._div.className = this._className;
    }
    else {
      this.addStyle();
    }

    this._div.style.zIndex = GOverlay.getZIndex(this.getLatLng().lat());
    this._div.innerHTML = this._html;
  }

  if (this._cursor) {
    this.setCursor(this._cursor);
  }

  this.setOpacity(this._opacity);
  var forgetZIndex = typeof this._zIndex === 'undefined';
  this.setZIndex(this.getZIndex(), forgetZIndex);

  this._div.style.display = 'none';
  map.getPane(this._pane).appendChild(this._div);
  this.getSize();
  this.redraw();

  if (!this._hide) {
    this._div.style.display = '';
  }

  return this._div;
};

BpLabel.prototype.remove = function() {
  if (this._div) {
    this._div.parentNode.removeChild(this._div);
  }
  delete this._map;
};

// this is a bare-bones implementation which anchors on the NW corner
BpLabel.prototype.redraw = function() {
  if (!this.isMapped()) {
    return;
  }

  var divPx = this._map.fromLatLngToDivPixel(this._latlng);

  if (this._anchor == 'center') {
    var size = this.getSize();
    if (size) {
      divPx.x -= Math.round(size.width/2);
      divPx.y -= Math.round(size.height/2);
    }
  }

  this._div.style.left = divPx.x + 'px';
  this._div.style.top  = divPx.y + 'px';
};

// latlng, html, opts (not userData, pane, cursor, opacity, zIndex)
BpLabel.prototype.copy = function() {
  return new BpLabel(this._latlng, this._html, this._hide, this._anchor);
};

BpLabel.prototype.getHtml = function() {
  return this._html;
};

BpLabel.prototype.setHtml = function(html) {
  this._stale_size = true;

  this._html = html;
  if (this._div) {
    this._div.innerHTML = this._html;
  }

  this.getSize();
};

BpLabel.prototype.isMapped = function() {
  return this._map ? true : false;
};

BpLabel.prototype.getMap = function() {
  return this._map;
};

BpLabel.prototype.show = function() {
  this._hide = false;
  if (this._div) {
    this._div.style.display = '';
  }
};

BpLabel.prototype.hide = function() {
  this._hide = true;
  if (this._div) {
    this._div.style.display = 'none';
  }
};

BpLabel.prototype.isVisible = function() {
  if (!this.isMapped() || !this._div || !this._div.parentNode) {
    return false;
  }

  return this._div.style.display != 'none';
};

BpLabel.prototype.isHidden = function() {
  return !this.isVisible();
};

BpLabel.prototype.getUserData = function() {
  return this._userData;
};

BpLabel.prototype.setUserData = function(userData) {
  this._userData = userData;
};

BpLabel.prototype.getZIndex = function() {
  if (! this._div) {
    if (typeof(this._zIndex) != 'undefined') {
      return this._zIndex;
    }
    else {
      return GOverlay.getZIndex(this.getLatLng().lat());
    }
  }

  return this._div.style.zIndex;
};

BpLabel.prototype.setZIndex = function(zIndex, forget) {
  if (arguments.length == 0) {
    this.resetZIndex();
    return;
  }

  if (!forget) {
    this._zIndex = zIndex;
  }

  if (this._div) {
    this._div.style.zIndex = zIndex;
  }
};

BpLabel.prototype.resetZIndex = function() {
  delete this._zIndex;
  var zIndex = GOverlay.getZIndex(this.getLatLng().lat());
  this.setZIndex(zIndex, true);
};

BpLabel.prototype.setHighZIndex = function() {
  var zIndex = GOverlay.getZIndex(-91);
  this.setZIndex(zIndex);
};

BpLabel.prototype.getOpacity = function() {
  return this._opacity;
};

BpLabel.prototype.setOpacity = function(opacity) {
  if(opacity < 0)   opacity = 0;
  if(opacity > 100) opacity = 100;

  this._opacity = opacity;

  if(this._div) {
    this._div.style.filter       = 'alpha(opacity:' + this._opacity + ')';
    this._div.style.KHTMLOpacity = this._opacity / 100;
    this._div.style.MozOpacity   = this._opacity / 100;
    this._div.style.opacity      = this._opacity / 100;
  }
};

BpLabel.prototype.getCursor = function() {
  return this._cursor;
};

BpLabel.prototype.setCursor = function(cursor) {
  this._cursor = cursor;

  if(this._div) {
    this._div.style.cursor = this._cursor;
  }
};

BpLabel.prototype.getPane = function() {
  return this._pane;
};

// only works before the overlay is added to the map
BpLabel.prototype.setPane = function(pane) {
  this._pane = pane;
};

BpLabel.prototype.getDiv = function() {
  return this._div;
};

BpLabel.prototype.getLatLng = function() {
  return this._latlng;
};

BpLabel.prototype.setLatLng = function(latlng) {
  this._latlng = latlng;
  if (typeof this._zIndex === 'undefined') {
    this.resetZIndex();
  }
  this.redraw();
};

BpLabel.prototype.getId = function() {
  return this._id;
};

BpLabel.prototype.getDiv = function() {
  return this._div;
};

BpLabel.prototype.draggingEnabled = function() {
  if (this._draggable) {
    return this._draggable.enabled();
  }
};

BpLabel.prototype.dragging = function() {
  if (this._draggable) {
    return this._draggable.dragging();
  }
};

BpLabel.prototype.disableDragging = function() {
  if (this._draggable) {
    this._draggable.disable();
    return this._draggable;
  }
};

BpLabel.prototype.enableDragging = function() {
  if (this._draggable) {
    this._draggable.enable();
    return this._draggable;
  }

  var label = this;
  var map = label.getMap();
  var draggable = new GDraggableObject(label.getDiv());
  this._draggable = draggable;
  if (label.getCursor()) {
    draggable.setDraggableCursor(label.getCursor());
    draggable.setDraggingCursor('move');
  }

  this._zoomend_listener = GEvent.addListener(map, 'zoomend', function() {
    var point = map.fromLatLngToDivPixel(label.getLatLng());
    if (label.getAnchor() == 'center') {
      point.x -= Math.ceil(label.getWidth() / 2);
      point.y -= Math.ceil(label.getHeight() / 2);
    }
    label._draggable.moveTo(point);
  });

  this._dragend_listener = GEvent.addListener(draggable, 'dragend', function() {
    var left = parseInt(label.getDiv().style.left, 10);
    var top = parseInt(label.getDiv().style.top, 10);

    if (label.getAnchor() == 'center') {
      if (!label.getSize()) {
        return; // we're off the map
      }
      left += Math.ceil(label.getWidth() / 2);
      top += Math.ceil(label.getHeight() / 2);
    }

    var point = new GPoint(left, top);
    var latlng = map.fromDivPixelToLatLng(point);
    label.setLatLng(latlng);
  });

  return draggable;
};

BpLabel.prototype.getDraggableObject = function() {
  return this._draggable;
};

window.BpLabel = BpLabel;
})();

