/*******************************************************************************
 * Copyright (c) 2000, 2010 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation 
 *     Mariot Chauvin <mariot.chauvin@obeo.fr> - bug 260740
 *******************************************************************************/
package org.eclipse.draw2d.geometry;

import org.eclipse.draw2d.PositionConstants;

/**
 * Represents a Rectangle(x, y, width, height). This class provides various
 * methods for manipulating this Rectangle or creating new derived geometrical
 * Objects.
 */
public class Rectangle implements Cloneable, java.io.Serializable, Translatable {

	private static final long serialVersionUID = 1L;

	/**
	 * A singleton for use in short calculations. Use to avoid newing
	 * unnecessary objects.
	 */
	public static final Rectangle SINGLETON = new Rectangle();

	/**
	 * The height
	 */
	public int height;

	/**
	 * The width
	 */
	public int width;

	/**
	 * The x value
	 */
	public int x;

	/**
	 * The y value
	 */
	public int y;

	/**
	 * Constructs a Rectangle at the origin with zero width and height.
	 * 
	 * @since 2.0
	 */
	public Rectangle() {
	}

	/**
	 * Constructs a Rectangle with the provided values.
	 * 
	 * @param x
	 *            X location
	 * @param y
	 *            Y location
	 * @param width
	 *            Width of the rectangle
	 * @param height
	 *            Height of the rectangle
	 * @since 2.0
	 */
	public Rectangle(int x, int y, int width, int height) {
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
	}

	/**
	 * Constructs a copy of the provided SWT
	 * {@link org.eclipse.swt.graphics.Rectangle}.
	 * 
	 * @param rect
	 *            The SWT Rectangle being copied
	 * @since 2.0
	 */
	public Rectangle(org.eclipse.swt.graphics.Rectangle rect) {
		this(rect.x, rect.y, rect.width, rect.height);
	}

	/**
	 * Constructs a Rectangle given a location and size.
	 * 
	 * @param p
	 *            the location
	 * @param size
	 *            the size
	 * @since 2.0
	 */
	public Rectangle(Point p, Dimension size) {
		this(p.x(), p.y(), size.width(), size.height());
	}

	/**
	 * Constructs the smallest Rectangle that contains the specified Points.
	 * 
	 * @param p1
	 *            Upper left hand corner
	 * @param p2
	 *            Lower right hand corner
	 * @since 2.0
	 */
	public Rectangle(Point p1, Point p2) {
		this.x = Math.min(p1.x(), p2.x());
		this.y = Math.min(p1.y(), p2.y());
		this.width = Math.abs(p2.x() - p1.x()) + 1;
		this.height = Math.abs(p2.y() - p1.y()) + 1;
	}

	/**
	 * Constructs a copy of the provided Rectangle.
	 * 
	 * @param rect
	 *            Rectangle supplying the initial values
	 * @since 2.0
	 */
	public Rectangle(Rectangle rect) {
		this(rect.x(), rect.y(), rect.width(), rect.height());
	}

	/**
	 * Returns the y-coordinate of the bottom of this Rectangle.
	 * 
	 * @return The Y coordinate of the bottom
	 * @since 2.0
	 */
	public int bottom() {
		return y + height;
	}

	/**
	 * Returns whether the given coordinates are within the boundaries of this
	 * Rectangle. The boundaries are inclusive of the top and left edges, but
	 * exclusive of the bottom and right edges.
	 * 
	 * @param x
	 *            X value
	 * @param y
	 *            Y value
	 * @return true if the coordinates are within this Rectangle
	 * @since 2.0
	 */
	public boolean contains(int x, int y) {
		return y >= this.y && y < this.y + this.height && x >= this.x
				&& x < this.x + this.width;
	}

	/**
	 * Returns whether the given point is within the boundaries of this
	 * Rectangle. The boundaries are inclusive of the top and left edges, but
	 * exclusive of the bottom and right edges.
	 * 
	 * @param p
	 *            Point being tested for containment
	 * @return true if the Point is within this Rectangle
	 * @since 2.0
	 */
	public boolean contains(Point p) {
		return contains(p.x(), p.y());
	}

	/**
	 * Returns <code>true</code> if the given rectangle is contained within the
	 * boundaries of this Rectangle.
	 * 
	 * @param rect
	 *            the Rectangle to test
	 * @return true if the Rectangle is within this Rectangle
	 */
	public boolean contains(Rectangle rect) {
		return x <= rect.x() && y <= rect.y() && right() >= rect.right()
				&& bottom() >= rect.bottom();
	}

	/**
	 * Crops this rectangle by the amount specified in <code>insets</code>.
	 * 
	 * @param insets
	 *            Insets to be removed from the Rectangle
	 * @return <code>this</code> for convenience
	 * @since 2.0
	 * @deprecated Use {@link #shrink(Insets)} instead.
	 */
	public Rectangle crop(Insets insets) {
		return shrink(insets);
	}

	/**
	 * Returns <code>true</code> if this Rectangle's x, y, width, and height
	 * values are identical to the provided ones.
	 * 
	 * @param x
	 *            The x value to test
	 * @param y
	 *            The y value to test
	 * @param width
	 *            The width value to test
	 * @param height
	 *            The height value to test
	 * @return <code>true</code> if this Rectangle's x, y, width, and height
	 *         values are identical to the provided ones, <code>false</code>
	 *         otherwise
	 * @since 3.7
	 */
	public boolean equals(int x, int y, int width, int height) {
		return this.x == x && this.y == y && this.width == width
				&& this.height == height;
	}

	/**
	 * Returns whether the input object is equal to this Rectangle or not.
	 * Rectangles are equivalent if their x, y, height, and width values are the
	 * same.
	 * 
	 * @param o
	 *            Object being tested for equality
	 * @return Returns the result of the equality test
	 * @since 2.0
	 */
	public boolean equals(Object o) {
		if (this == o)
			return true;
		if (o instanceof Rectangle) {
			Rectangle r = (Rectangle) o;
			return (x == r.x()) && (y == r.y()) && (width == r.width())
					&& (height == r.height());
		}
		return false;
	}

	/**
	 * Expands the horizontal and vertical sides of this Rectangle by the width
	 * and height of the given Insets, and returns this for convenience.
	 * 
	 * @param insets
	 *            contains the amounts to expand on each side
	 * @return <code>this</code> for convenience
	 * @since 2.0
	 */
	public Rectangle expand(Insets insets) {
		x -= insets.left;
		y -= insets.top;
		width += insets.getWidth();
		height += insets.getHeight();
		return this;
	}

	/**
	 * Expands the horizontal and vertical sides of this Rectangle with the
	 * values provided as input, and returns this for convenience. The location
	 * of its center is kept constant.
	 * 
	 * @param h
	 *            Horizontal increment
	 * @param v
	 *            Vertical increment
	 * @return <code>this</code> for convenience
	 * @since 2.0
	 */
	public Rectangle expand(int h, int v) {
		return shrink(-h, -v);
	}

	/**
	 * Returns a new Point representing the middle point of the bottom side of
	 * this Rectangle.
	 * 
	 * @return Point at the bottom of the Rectangle
	 * @since 2.0
	 */
	public Point getBottom() {
		return new Point(x + width / 2, bottom());
	}

	/**
	 * Returns a new Point representing the bottom left point of this Rectangle.
	 * 
	 * @return Point at the bottom left of the rectangle
	 * @since 2.0
	 */
	public Point getBottomLeft() {
		return new Point(x, y + height);
	}

	/**
	 * Returns a new Point representing the bottom right point of this
	 * Rectangle.
	 * 
	 * @return Point at the bottom right of the rectangle
	 * @since 2.0
	 */
	public Point getBottomRight() {
		return new Point(x + width, y + height);
	}

	/**
	 * Returns a new point representing the center of this Rectangle.
	 * 
	 * @return Point at the center of the rectangle
	 */
	public Point getCenter() {
		return new Point(x + width / 2, y + height / 2);
	}

	/**
	 * Returns a new Rectangle which has the exact same parameters as this
	 * Rectangle.
	 * 
	 * @return Copy of this Rectangle
	 * @since 2.0
	 */
	public Rectangle getCopy() {
		// FIXME: This is inconsistent with the behavior of getCopy() within
		// Point and Dimension and should only return a new Rectangle, leaving
		// the property copying of others to subclasses. Clients should
		// not relay on this method performing a clone()
		if (getClass() == Rectangle.class) {
			/* avoid clone() call cost see bug #260740 */
			return new Rectangle(this);
		} else {
			try {
				return (Rectangle) clone();
			} catch (CloneNotSupportedException exc) {
				return new Rectangle(this);
			}
		}
	}

	/**
	 * Returns a new Rectangle with the specified insets cropped.
	 * 
	 * @param insets
	 *            Insets being cropped from the Rectangle
	 * @return Cropped new Rectangle
	 * @deprecated Use {@link #getShrinked(Insets)} instead.
	 */
	public Rectangle getCropped(Insets insets) {
		return getShrinked(insets);
	}

	/**
	 * Creates and returns a new Rectangle with the bounds of <code>this</code>
	 * Rectangle, expanded by the given Insets.
	 * 
	 * @param insets
	 *            The insets used to expand this rectangle
	 * @return A new expanded Rectangle
	 * @since 2.0
	 */
	public Rectangle getExpanded(Insets insets) {
		return getCopy().expand(insets);
	}

	/**
	 * Returns a new incremented Rectangle, where the sides are expanded by the
	 * horizontal and vertical values provided. The center of the Rectangle is
	 * maintained constant.
	 * 
	 * @param h
	 *            Horizontal increment
	 * @param v
	 *            Vertical increment
	 * @return A new expanded Rectangle
	 * @since 2.0
	 */
	public Rectangle getExpanded(int h, int v) {
		return getCopy().expand(h, v);
	}

	/**
	 * Returns a new Rectangle which has the intersection of this Rectangle and
	 * the rectangle provided as input. Returns an empty Rectangle if there is
	 * no intersection.
	 * 
	 * @param rect
	 *            Rectangle provided to test for intersection
	 * @return A new Rectangle representing the intersection
	 * @since 2.0
	 */
	public Rectangle getIntersection(Rectangle rect) {
		return getCopy().intersect(rect);
	}

	/**
	 * Returns a new Point representing the middle point of the left hand side
	 * of this Rectangle.
	 * 
	 * @return Point at the left of the Rectangle
	 */
	public Point getLeft() {
		return new Point(x, y + height / 2);
	}

	/**
	 * Returns the upper left hand corner of the rectangle.
	 * 
	 * @return Location of the rectangle
	 * @see #setLocation(Point)
	 */
	public Point getLocation() {
		return new Point(x, y);
	}

	/**
	 * <P>
	 * Returns an integer which represents the position of the given point with
	 * respect to this rectangle. Possible return values are bitwise ORs of the
	 * constants WEST, EAST, NORTH, and SOUTH as found in
	 * {@link org.eclipse.draw2d.PositionConstants}.
	 * 
	 * <P>
	 * Returns PositionConstant.NONE if the given point is inside this
	 * Rectangle.
	 * 
	 * @param p
	 *            The Point whose position has to be determined
	 * @return An <code>int</code> which is a PositionConstant
	 * @see org.eclipse.draw2d.PositionConstants
	 * @since 2.0
	 */
	public int getPosition(Point p) {
		int result = PositionConstants.NONE;

		if (contains(p))
			return result;

		if (p.x() < x)
			result = PositionConstants.WEST;
		else if (p.x() >= (x + width))
			result = PositionConstants.EAST;

		if (p.y() < y)
			result = result | PositionConstants.NORTH;
		else if (p.y() >= (y + height))
			result = result | PositionConstants.SOUTH;

		return result;
	}

	/**
	 * Returns a new Rectangle which is equivalent to this Rectangle with its
	 * dimensions modified by the passed Dimension <i>d</i>.
	 * 
	 * @param d
	 *            Dimensions by which the rectangle's size should be modified
	 * @return The new rectangle with the modified dimensions
	 * @since 2.0
	 */
	public Rectangle getResized(Dimension d) {
		return getCopy().resize(d);
	}

	/**
	 * Returns a new Rectangle which is equivalent to this Rectangle with its
	 * dimensions modified by the passed width <i>w</i> and height <i>h</i>.
	 * 
	 * @param w
	 *            Amount by which width is to be resized
	 * @param h
	 *            Amount by which height is to be resized
	 * @return a new rectangle with its width and height modified
	 */
	public Rectangle getResized(int w, int h) {
		return getCopy().resize(w, h);
	}

	/**
	 * Returns a new Point which represents the middle point of the right hand
	 * side of this Rectangle.
	 * 
	 * @return Point at the right of the Rectangle
	 * @since 2.0
	 */
	public Point getRight() {
		return new Point(right(), y + height / 2);
	}

	/**
	 * Returns a new Rectangle shrinked by the specified insets.
	 * 
	 * @param insets
	 *            Insets being cropped from the Rectangle
	 * @return Shrinked new Rectangle
	 * @since 3.7
	 */
	public Rectangle getShrinked(Insets insets) {
		return getCopy().shrink(insets);
	}

	/**
	 * Returns a new Rectangle, where the sides are shrinked by the horizontal
	 * and vertical values supplied. The center of this Rectangle is kept
	 * constant.
	 * 
	 * @param h
	 *            Horizontal reduction amount
	 * @param v
	 *            Vertical reduction amount
	 * @return <code>this</code> for convenience
	 * @since 3.7
	 */
	public Rectangle getShrinked(int h, int v) {
		return getCopy().shrink(h, v);
	}

	/**
	 * Retuns the dimensions of this Rectangle.
	 * 
	 * @return Size of this Rectangle as a Dimension
	 * @since 2.0
	 */
	public Dimension getSize() {
		return new Dimension(width, height);
	}

	/**
	 * Returns a new Point which represents the middle point of the top side of
	 * this Rectangle.
	 * 
	 * @return Point at the top of the Rectangle
	 * @since 2.0
	 */
	public Point getTop() {
		return new Point(x + width / 2, y);
	}

	/**
	 * Returns a new Point which represents the top left hand corner of this
	 * Rectangle.
	 * 
	 * @return Point at the top left of the rectangle
	 * @since 2.0
	 */
	public Point getTopLeft() {
		return new Point(x, y);
	}

	/**
	 * Returns a new Point which represents the top right hand corner of this
	 * Rectangle.
	 * 
	 * @return Point at the top right of the rectangle
	 * @since 2.0
	 */
	public Point getTopRight() {
		return new Point(x + width, y);
	}

	/**
	 * Returns a new Rectangle which is shifted along each axis by the passed
	 * values.
	 * 
	 * @param dx
	 *            Displacement along X axis
	 * @param dy
	 *            Displacement along Y axis
	 * @return The new translated rectangle
	 * @since 2.0
	 */
	public Rectangle getTranslated(int dx, int dy) {
		return getCopy().translate(dx, dy);
	}

	/**
	 * Returns a new Rectangle which is shifted by the position of the given
	 * Point.
	 * 
	 * @param pt
	 *            Point providing the amount of shift along each axis
	 * @return The new translated Rectangle
	 * @since 2.0
	 */
	public Rectangle getTranslated(Point pt) {
		return getCopy().translate(pt);
	}

	/**
	 * Returns a new rectangle whose width and height have been interchanged, as
	 * well as its x and y values. This can be useful in orientation changes.
	 * 
	 * @return The transposed rectangle
	 * @since 2.0
	 */
	public Rectangle getTransposed() {
		return getCopy().transpose();
	}

	/**
	 * Returns a new Rectangle which contains both this Rectangle and the Point
	 * supplied as input.
	 * 
	 * @param p
	 *            Point for calculating union
	 * @return A new unioned Rectangle
	 * @since 3.7
	 */
	public Rectangle getUnion(Point p) {
		Rectangle copy = getCopy();
		copy.union(p);
		return copy;
	}

	/**
	 * Returns a new Rectangle which contains both this Rectangle and the
	 * Rectangle supplied as input.
	 * 
	 * @param rect
	 *            Rectangle for calculating union
	 * @return A new unioned Rectangle
	 * @since 2.0
	 */
	public Rectangle getUnion(Rectangle rect) {
		return getCopy().union(rect);
	}

	/**
	 * @see java.lang.Object#hashCode()
	 */
	public int hashCode() {
		return (x + height + 1) * (y + width + 1) ^ x ^ y;
	}

	/**
	 * Returns the current height of this Rectangle
	 * 
	 * @return The current height
	 * @since 3.7
	 */
	public int height() {
		return height;
	}

	/**
	 * Sets the size of this Rectangle to the intersection region with the
	 * Rectangle supplied as input, and returns this for convenience. The
	 * location and dimensions are set to zero if there is no intersection with
	 * the input Rectangle.
	 * 
	 * @param rect
	 *            Rectangle for the calculating intersection.
	 * @return <code>this</code> for convenience
	 * @since 2.0
	 */
	public Rectangle intersect(Rectangle rect) {
		int x1 = Math.max(x, rect.x());
		int x2 = Math.min(x + width, rect.x() + rect.width());
		int y1 = Math.max(y, rect.y());
		int y2 = Math.min(y + height, rect.y() + rect.height());
		if (((x2 - x1) < 0) || ((y2 - y1) < 0))
			return setBounds(0, 0, 0, 0); // no intersection
		else {
			return setBounds(x1, y1, x2 - x1, y2 - y1);
		}
	}

	/**
	 * Returns <code>true</code> if the input Rectangle intersects this
	 * Rectangle.
	 * 
	 * @param rect
	 *            Rectangle for the intersection test
	 * @return <code>true</code> if the input Rectangle intersects this
	 *         Rectangle
	 * @since 2.0
	 */
	public boolean intersects(Rectangle rect) {
		return !getIntersection(rect).isEmpty();
	}

	/**
	 * Returns <code>true</code> if this Rectangle's width or height is less
	 * than or equal to 0.
	 * 
	 * @return <code>true</code> if this Rectangle is empty
	 * @since 2.0
	 */
	public boolean isEmpty() {
		return width <= 0 || height <= 0;
	}

	/**
	 * @see Translatable#performScale(double)
	 */
	public void performScale(double factor) {
		scale(factor);
	}

	/**
	 * @see Translatable#performTranslate(int, int)
	 */
	public void performTranslate(int dx, int dy) {
		translate(dx, dy);
	}

	/**
	 * Returns <code>double</code> height
	 * 
	 * @return <code>double</code> height
	 * @since 3.4
	 */
	public double preciseHeight() {
		return height;
	}

	/**
	 * Returns <code>double</code> width
	 * 
	 * @return <code>double</code> width
	 * @since 3.4
	 */
	public double preciseWidth() {
		return width;
	}

	/**
	 * Returns <code>double</code> x coordinate
	 * 
	 * @return <code>double</code> x coordinate
	 * @since 3.4
	 */
	public double preciseX() {
		return x;
	}

	/**
	 * Returns <code>double</code> y coordinate
	 * 
	 * @return <code>double</code> y coordinate
	 * @since 3.4
	 */
	public double preciseY() {
		return y;
	}

	/**
	 * Resizes this Rectangle by the Dimension provided as input and returns
	 * this for convenience. This Rectange's width will become this.width +
	 * sizeDelta.width. Likewise for height.
	 * 
	 * @param d
	 *            Resize data as a Dimension
	 * @return <code>this</code> for convenience
	 * @since 2.0
	 */
	public Rectangle resize(Dimension d) {
		width += d.width();
		height += d.height();
		return this;
	}

	/**
	 * Resizes this Rectangle by the values supplied as input and returns this
	 * for convenience. This Rectangle's width will become this.width + dw. This
	 * Rectangle's height will become this.height + dh.
	 * 
	 * @param w
	 *            Amount by which width is to be resized
	 * @param h
	 *            Amount by which height is to be resized
	 * @return <code>this</code> for convenience
	 * @since 2.0
	 */
	public Rectangle resize(int w, int h) {
		width += w;
		height += h;
		return this;
	}

	/**
	 * Returns the x-coordinate of the right side of this Rectangle.
	 * 
	 * @return The X coordinate of the right side
	 * @since 2.0
	 */
	public int right() {
		return x + width;
	}

	/**
	 * Scales the location and size of this Rectangle by the given scale and
	 * returns this for convenience.
	 * 
	 * @param scaleFactor
	 *            The factor by which this rectangle will be scaled
	 * @return <code>this</code> for convenience
	 * @since 2.0
	 */
	public final Rectangle scale(double scaleFactor) {
		return scale(scaleFactor, scaleFactor);
	}

	/**
	 * Scales the location and size of this Rectangle by the given scales and
	 * returns this for convenience.
	 * 
	 * @param scaleX
	 *            the factor by which the X dimension has to be scaled
	 * @param scaleY
	 *            the factor by which the Y dimension has to be scaled
	 * @return <code>this</code> for convenience
	 * @since 2.0
	 */
	public Rectangle scale(double scaleX, double scaleY) {
		int oldX = x;
		int oldY = y;
		x = (int) (Math.floor(x * scaleX));
		y = (int) (Math.floor(y * scaleY));
		width = (int) (Math.ceil((oldX + width) * scaleX)) - x;
		height = (int) (Math.ceil((oldY + height) * scaleY)) - y;
		return this;
	}

	/**
	 * Sets the x, y, width, and height values of this Rectangle to the provided
	 * values.
	 * 
	 * @param x
	 *            The new x
	 * @param y
	 *            The new y
	 * @param width
	 *            The new width
	 * @param height
	 *            The new height
	 * @return this for convenience
	 * @since 3.7
	 */
	public Rectangle setBounds(int x, int y, int width, int height) {
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
		return this;
	}

	/**
	 * Sets the location and size of this rectangle to the provided ones.
	 * 
	 * @param location
	 *            The new location
	 * @param size
	 *            The new size
	 * @return this for convenience
	 * @since 3.7
	 */
	public Rectangle setBounds(Point location, Dimension size) {
		return setBounds(location.x(), location.y(), size.width(),
				size.height());
	}

	/**
	 * Sets the parameters of this Rectangle from the Rectangle passed in and
	 * returns this for convenience.
	 * 
	 * @return <code>this</code> for convenience
	 * @param rect
	 *            Rectangle providing the bounding values
	 * @since 2.0
	 */
	public Rectangle setBounds(Rectangle rect) {
		return setBounds(rect.x(), rect.y(), rect.width(), rect.height());
	}

	/**
	 * Sets the height of this Rectangle to the specified one.
	 * 
	 * @param height
	 *            The new height
	 * @return this for convenience.
	 * @since 3.7
	 */
	public Rectangle setHeight(int height) {
		this.height = height;
		return this;
	}

	/**
	 * Sets the location of this Rectangle to the coordinates given as input and
	 * returns this for convenience.
	 * 
	 * @param x
	 *            The new X coordinate
	 * @param y
	 *            The new Y coordinate
	 * @return <code>this</code> for convenience
	 * @since 2.0
	 */
	public Rectangle setLocation(int x, int y) {
		this.x = x;
		this.y = y;
		return this;
	}

	/**
	 * Sets the location of this Rectangle to the point given as input and
	 * returns this for convenience.
	 * 
	 * @return <code>this</code> for convenience
	 * @param p
	 *            New position of this Rectangle
	 * @since 2.0
	 */
	public Rectangle setLocation(Point p) {
		return setLocation(p.x(), p.y());
	}

	/**
	 * Sets the width and height of this Rectangle to the width and height of
	 * the given Dimension and returns this for convenience.
	 * 
	 * @param d
	 *            The new Dimension
	 * @return <code>this</code> for convenience
	 * @since 2.0
	 */
	public Rectangle setSize(Dimension d) {
		return setSize(d.width(), d.height());
	}

	/**
	 * Sets the width of this Rectangle to <i>w</i> and the height of this
	 * Rectangle to <i>h</i> and returns this for convenience.
	 * 
	 * @return <code>this</code> for convenience
	 * @param w
	 *            The new width
	 * @param h
	 *            The new height
	 * @since 2.0
	 */
	public Rectangle setSize(int w, int h) {
		width = w;
		height = h;
		return this;
	}

	/**
	 * Sets the width of this Rectangle to the specified one.
	 * 
	 * @param width
	 *            The new width
	 * @return this for convenience.
	 * @since 3.7
	 */
	public Rectangle setWidth(int width) {
		this.width = width;
		return this;
	}

	/**
	 * Sets the x value of the Rectangle and returns this for convenience.
	 * 
	 * @return <code>this</code> for convenience
	 * @param x
	 *            The new x value
	 * @since 3.7
	 */
	public Rectangle setX(int x) {
		this.x = x;
		return this;
	}

	/**
	 * Sets the y value of the Rectangle and returns this for convenience.
	 * 
	 * @return <code>this</code> for convenience
	 * @param y
	 *            The new y value
	 * @since 3.7
	 */
	public Rectangle setY(int y) {
		this.y = y;
		return this;
	}

	/**
	 * Shrinks this rectangle by the amount specified in <code>insets</code>.
	 * 
	 * @param insets
	 *            Insets to be removed from the Rectangle
	 * @return <code>this</code> for convenience
	 * @since 3.7
	 */
	public Rectangle shrink(Insets insets) {
		if (insets == null)
			return this;
		x += insets.left;
		y += insets.top;
		width -= (insets.getWidth());
		height -= (insets.getHeight());
		return this;
	}

	/**
	 * Shrinks the sides of this Rectangle by the horizontal and vertical values
	 * provided as input, and returns this Rectangle for convenience. The center
	 * of this Rectangle is kept constant.
	 * 
	 * @param h
	 *            Horizontal reduction amount
	 * @param v
	 *            Vertical reduction amount
	 * @return <code>this</code> for convenience
	 * @since 2.0
	 */
	public Rectangle shrink(int h, int v) {
		x += h;
		width -= (h + h);
		y += v;
		height -= (v + v);
		return this;
	}

	/**
	 * Returns the description of this Rectangle.
	 * 
	 * @return String containing the description
	 * @since 2.0
	 */
	public String toString() {
		return "Rectangle(" + x + ", " + y + ", " + //$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$
				width + ", " + height + ")";//$NON-NLS-2$//$NON-NLS-1$
	}

	/**
	 * Returns <code>true</code> if the input Rectangle touches this Rectangle.
	 * 
	 * @param rect
	 *            Rectangle being checked for contact
	 * @return <code>true</code> if rect touches this Rectangle
	 * @since 2.0
	 */
	public boolean touches(Rectangle rect) {
		return rect.x() <= x + width && rect.y() <= y + height
				&& rect.x() + rect.width() >= x
				&& rect.y() + rect.height() >= y;
	}

	/**
	 * Moves this Rectangle horizontally by dx and vertically by dy, then
	 * returns this Rectangle for convenience.
	 * 
	 * @param dx
	 *            Shift along X axis
	 * @param dy
	 *            Shift along Y axis
	 * @return <code>this</code> for convenience
	 * @since 2.0
	 */
	public Rectangle translate(int dx, int dy) {
		x += dx;
		y += dy;
		return this;
	}

	/**
	 * Moves this Rectangle horizontally by the x value of the given Point and
	 * vertically by the y value of the given Point, then returns this Rectangle
	 * for convenience.
	 * 
	 * @param p
	 *            Point which provides translation information
	 * @return <code>this</code> for convenience
	 */
	public Rectangle translate(Point p) {
		x += p.x;
		y += p.y;
		return this;
	}

	/**
	 * Switches the x and y values, as well as the width and height of this
	 * Rectangle. Useful for orientation changes.
	 * 
	 * @return <code>this</code> for convenience
	 * @since 2.0
	 */
	public Rectangle transpose() {
		int temp = x;
		x = y;
		y = temp;
		temp = width;
		width = height;
		height = temp;
		return this;
	}

	/**
	 * Unions this Rectangle's width and height with the specified Dimension.
	 * 
	 * @param d
	 *            Dimension being unioned
	 * @return <code>this</code> for convenience
	 * @since 2.0
	 * @deprecated Union with a dimension generally does not make much sense,
	 *             thus deprecating this. Use
	 *             {@link Dimension#max(Dimension, Dimension)} and
	 *             {@link #setSize(Dimension)} to implement the desired behavior
	 *             instead.
	 */
	public Rectangle union(Dimension d) {
		width = Math.max(width, d.width);
		height = Math.max(height, d.height);
		return this;
	}

	/**
	 * Updates this Rectangle's bounds to the minimum size which can hold both
	 * this Rectangle and the coordinate (x,y).
	 * 
	 * @return <code>this</code> for convenience
	 * @param x1
	 *            X coordinate
	 * @param y1
	 *            Y coordinate
	 * @since 2.0
	 */
	public Rectangle union(int x1, int y1) {
		if (x1 < x) {
			width += (x - x1);
			x = x1;
		} else {
			int right = x + width;
			if (x1 >= right) {
				right = x1 + 1;
				width = right - x;
			}
		}
		if (y1 < y) {
			height += (y - y1);
			y = y1;
		} else {
			int bottom = y + height;
			if (y1 >= bottom) {
				bottom = y1 + 1;
				height = bottom - y;
			}
		}
		return this;
	}

	/**
	 * Updates this Rectangle's dimensions to the minimum size which can hold
	 * both this Rectangle and the rectangle (x, y, w, h).
	 * 
	 * @param x
	 *            X coordinate of desired union.
	 * @param y
	 *            Y coordinate of desired union.
	 * @param w
	 *            Width of desired union.
	 * @param h
	 *            Height of desired union.
	 * @return <code>this</code> for convenience
	 * @since 2.0
	 */
	public Rectangle union(int x, int y, int w, int h) {
		int right = Math.max(this.x + width, x + w);
		int bottom = Math.max(this.y + height, y + h);
		this.x = Math.min(this.x, x);
		this.y = Math.min(this.y, y);
		this.width = right - this.x;
		this.height = bottom - this.y;
		return this;
	}

	/**
	 * Updates this Rectangle's bounds to the minimum size which can hold both
	 * this Rectangle and the given Point.
	 * 
	 * @param p
	 *            Point to be unioned with this Rectangle
	 * @since 2.0
	 */
	public void union(Point p) {
		// TODO: This should for the sake of consistency also return this
		// by convenience; however, this is regarded as API break so it
		// may be done not sooner than in 4.0; if this is done, it has
		// to be ensured that the overwritten method in PrecisionRectangle
		// is adjusted as well.
		union(p.x(), p.y());
	}

	/**
	 * Updates this Rectangle's dimensions to the minimum size which can hold
	 * both this Rectangle and the given Rectangle.
	 * 
	 * @return <code>this</code> for convenience
	 * @param rect
	 *            Rectangle to be unioned with this Rectangle
	 * @since 2.0
	 */
	public Rectangle union(Rectangle rect) {
		if (rect == null || rect.isEmpty())
			return this;
		return union(rect.x, rect.y, rect.width, rect.height);
	}

	/**
	 * Returns the current width of this Rectangle
	 * 
	 * @return The current width
	 * @since 3.7
	 */
	public int width() {
		return width;
	}

	/**
	 * Returns the x value of this Rectangle.
	 * 
	 * @return The current x value
	 * @since 3.7
	 */
	public int x() {
		return x;
	}

	/**
	 * Returns the y value of the Rectangle
	 * 
	 * @return The current y value
	 * @since 3.7
	 */
	public int y() {
		return y;
	}

}
