// Copyright 2007 Google Inc. All Rights Reserved.


var CSS_borderLeft = "borderLeft";
var CSS_borderTop  = "borderTop";
var CSS_borderRight = "borderRight";
var CSS_borderBottom = "borderBottom";
var CSS_border = "border";
var CSS_width = "width";
var CSS_height = "height";
var CSS_backgroundColor = "backgroundColor";


function Rectangle(bounds, opt_weight, opt_color, opt_hiliteColor,
                   opt_shadowColor, opt_backgroundColor, opt_opacity,
                   opt_draggable, opt_parentPane ) {
  this.bounds_ = bounds;
  this.weight_ = opt_weight;
  this.color_ = opt_color || "#979797";
  var border3d = "1px solid ";
  this.hiliteBorderStyle_ = border3d + (opt_hiliteColor || "#AAAAAA");
  this.shadowBorderStyle_ = border3d + (opt_shadowColor || "#777777");
  this.backgroundColor_ = opt_backgroundColor || "white";
  this.opacity_ = opt_opacity || 0.01;
  this.draggable_ = opt_draggable;
  this.parentPane = opt_parentPane;
}

delegate(Rectangle, GOverlay);

/**
 * Creates the DIV representing this rectangle. The only complex part is
 * setting up dragging if this rectangle is draggable.
 * opt_parent allows the rectangle to be attached to a different parent
 * instead of the map, to prevent it from moving with the map.
 *
 * @param {Map} map  The map.
 * @param {DOM Element} opt_parent  The parent node (optional).
 */
Rectangle.prototype.initialize = function(map, opt_parent) {
  var me = this;
  me.map_ = map;

  var div = createElement("div", me.parentPane || map.getPane(G_MAP_MAP_PANE),
                          null, GSize.ZERO);
  div.style[CSS_borderLeft]   = me.hiliteBorderStyle_;
  div.style[CSS_borderTop]    = me.hiliteBorderStyle_;
  div.style[CSS_borderRight]  = me.shadowBorderStyle_;
  div.style[CSS_borderBottom] = me.shadowBorderStyle_;

  var borderDiv = createElement("div", div);
  borderDiv.style[CSS_border] = px(me.weight_) + " solid " + me.color_;
  borderDiv.style[CSS_width] = "100%";
  borderDiv.style[CSS_height] = "100%";
  overflowHidden(borderDiv);
  me.borderDiv_ = borderDiv;

  var colorDiv = createElement("div", borderDiv);
  colorDiv.style[CSS_width] = "100%";
  colorDiv.style[CSS_height] = "100%";
  // Opera does not support transparency, so leave the background empty
  if (gBrowser.type != BROWSER_OPERA) {
    colorDiv.style[CSS_backgroundColor] = me.backgroundColor_;
  }
  setOpacity(colorDiv, me.opacity_);
  me.colorDiv_ = colorDiv;

  var dragObject = new DraggableObject(div);
  me.dragObject_ = dragObject;
  dragObject.disable();

  me.consistent_ = true;
  me.div_ = div;
};

/**
 * Removes this rectangle from the map.
 *
 * @param {Boolean} force  Forces removal (unnecessary).
 */
Rectangle.prototype.remove = function(force) {
  var node = this.div_;
  if (node.parentNode) {
    node.parentNode.removeChild(node);
  }
};

/**
 * Hides this rectangle.
 */
Rectangle.prototype.hide = function() {
  visibilityHidden(this.div_);
};

/**
 * Shows this rectangle.
 */
Rectangle.prototype.show = function() {
  visibilityDefault(this.div_);
};

/**
 * Copies this rectangle.
 *
 * @return {Rectangle}  Copy of this rectangle.
 */
Rectangle.prototype.copy = function() {
  return new Rectangle(this.getBounds(), this.weight_, this.color_,
                       this.hiliteColor_, this.shadowColor_,
                       this.backgroundColor_, this.opacity_, this.draggable_);
};

/**
 * Redraws this rectangle.
 *
 * @param {Boolean} force  Forces a redraw.
 */
Rectangle.prototype.redraw = function(force) {
  if (!force) return;
  var me = this;

  if (me.dragging_) return;

  var div = me.div_;
  var map = me.map_;
  var weight = me.weight_;
  var bounds = me.getBounds();

  // Use the center longitude to be sure we draw the rectangle around its
  // center, rather than wrapped around the globe
  var center = bounds.getCenter();
  var centerDiv = map.fromLatLngToDivPixel(center);

  // Calculate the DIV coordinates of two opposite corners of our
  // bounds to get the size and position of our rectangle
  var sw = map.fromLatLngToDivPixel(bounds.getSouthWest(), centerDiv);
  var ne = map.fromLatLngToDivPixel(bounds.getNorthEast(), centerDiv);
  var minx = Math.min(sw.x,ne.x);
  var maxx = Math.max(sw.x,ne.x);
  var miny = Math.min(sw.y,ne.y);
  var maxy = Math.max(sw.y,ne.y);

  // Special-case full bounding boxes
  if( bounds.isFullLng() ) {
    minx = -5;
    maxx = map.getContainer().clientWidth + 5;
  }
  if( bounds.isFullLat() ) {
    miny = -5;
    maxy = map.getContainer().clientHeight + 5;
  }

  // Clamp to the visible map bounds, because some platforms can't 
  // cope with very large divs.  The 5-pixel padding makes room for 
  // the 2-pixel borders when the rectangle is off to one side.
  var mapBounds = map.getBounds();
  var mapCenterDiv = map.fromLatLngToDivPixel(map.getBounds().getCenter());
  var mapsw = map.fromLatLngToDivPixel(mapBounds.getSouthWest(), mapCenterDiv);
  var mapne = map.fromLatLngToDivPixel(mapBounds.getNorthEast(), mapCenterDiv);

  if (!map.getBounds().isFullLng()) {
    minx = Math.max(minx,Math.min(mapsw.x,mapne.x)-5);
    maxx = Math.min(maxx,Math.max(mapsw.x,mapne.x)+5);
  }
  if (!mapBounds.isFullLat()) {
    miny = Math.max(miny,Math.min(mapsw.y,mapne.y)-5);
    maxy = Math.min(maxy,Math.max(mapsw.y,mapne.y)+5);
  }

  // This would be +1 but for the 1px border3d on each side
  // Negative numbers cause problems on some platforms
  this.setSize(new GSize(Math.max(maxx-minx-1,0), Math.max(maxy-miny-1,0)));
  me.dragObject_.moveTo(minx, miny);
};

/**
 * Sets the size of this rectangle.
 *
 * @param {Size} size  The desired size.
 */
Rectangle.prototype.setSize = function(size) {
  setSize(this.div_, size);

  // The borderDiv should fit just exactly inside the main div.
  // FF doesn't handle this properly, so we do it manually:
  var innerSize =  new GSize(
      Math.max(0, size.width  - 2 * this.weight_),  /* Don't allow negative size */
      Math.max(0, size.height - 2 * this.weight_));
  setSize(this.borderDiv_, innerSize);
  setSize(this.colorDiv_, innerSize);
};

/**
 * Copies the size of this rectangle from the target rectangle.
 *
 * @param {Rectangle} rect  The target rectangle.
 */
Rectangle.prototype.duplicateSize = function(rect) {
  var size = new GSize(rect.div_.clientWidth, rect.div_.clientHeight);
  this.setSize(size);
};

/**
 * Centers this rectangle inside its container.
 */
Rectangle.prototype.centerInContainer = function() {
  var parent = this.div_.parentNode;
  var x = round((parent.clientWidth - this.div_.offsetWidth) / 2);
  var y = round((parent.clientHeight - this.div_.offsetHeight) / 2);
  this.dragObject_.moveTo(x, y);
};

/**
 * Sets the lat/lng bounds for this rectangle.
 *
 * @param {LatLngBounds} bounds  The new bounds.
 */
Rectangle.prototype.setBounds = function(bounds) {
  this.bounds_ = bounds;
  this.consistent_ = true;
  this.redraw(true);
};

/**
 * Sets the center for this rectangle.
 *
 * @param {LatLng} latlng  The new center.
 */
Rectangle.prototype.setCenter = function(latlng) {
  var divPixel = this.map_.fromLatLngToDivPixel(latlng);
  this.dragObject_.moveTo(divPixel.x - round(this.div_.offsetWidth / 2),
                          divPixel.y - round(this.div_.offsetHeight / 2) );
  this.consistent_ = false;
};

/**
 * Returns the bounds for this rectangle.
 *
 * @return {LatLngBounds}  The bounds of this rectangle.
 */
Rectangle.prototype.getBounds = function() {
  if (!this.consistent_) {
    this.resetBounds_();
  }
  return this.bounds_;
};

/**
 * Gets the center of the div
 *
 * @return {Point}  The center of the div.
 */
Rectangle.prototype.getDivCenter = function() {
  var dragObject = this.dragObject_;
  return new GPoint(dragObject.left + round(this.div_.offsetWidth / 2),
                   dragObject.top + round(this.div_.offsetHeight / 2));
};

/**
 * Returns the lat/lng center of the bounds.
 *
 * @return {LatLng}  The center of the bounds.
 */
Rectangle.prototype.getCenter = function() {
  return this.map_.fromDivPixelToLatLng(this.getDivCenter());
};

/**
 * When the rectangle is draggable, we need to reset our bounds after a drag
 * because our DIV position is not consistent with our lat/lng bounds. This
 * function re-computes our lat/lng bounds from our position and size in the
 * map pane DIV.
 */
Rectangle.prototype.resetBounds_ = function() {
  var map = this.map_;
  var divBounds = this.getDivBounds();
  this.setBounds(new GLatLngBounds(
      map.fromDivPixelToLatLng(divBounds.min()),
      map.fromDivPixelToLatLng(divBounds.max())));
};

/**
 * When we drag, our lat/lng bounds is no longer consistent with our position
 * in the map pane.
 */
Rectangle.prototype.onDrag_ = function() {
  this.consistent_ = false;
};

/**
 * Handle onDragStart event.
 */
Rectangle.prototype.onDragStart_ = function() {
  this.dragging_ = true;
};

/**
 * Handle onDragEnd event.
 */
Rectangle.prototype.onDragEnd_ = function() {
  this.dragging_ = false;
  this.redraw(true);  // Update the position if necessary
};

/**
 * Returns the bounds of the div.
 *
 * @return {Bounds}  Div bounds (in pixel coordinates).
 */
Rectangle.prototype.getDivBounds = function() {
  var dragObject = this.dragObject_;
  var weight = this.weight_;
  var bl = new GPoint(dragObject.left + weight,
                     dragObject.top + this.div_.offsetHeight - weight);
  var tr = new GPoint(dragObject.left + this.div_.offsetWidth - weight,
                     dragObject.top + weight);
  return new Bounds([bl, tr]);
};

/**
 * Sets the cursor for this rectangle.
 *
 * @param {String} cursor  The new cursor.
 */
Rectangle.prototype.setCursor = function(cursor) {
  setCursor(this.div_, cursor);
}

Rectangle.prototype.getDiv= function() {
  return(this.div_);
};
