package nxm.sys.libg;

import java.awt.Rectangle;

import nxm.sys.inc.Keyable;
import nxm.sys.lib.Convert;

/** A class to define a box.
    @author Jeff Schoen
*/
public class MBox extends Object implements Keyable {
  /** The keys available for the class. */
  private static final String[] keys = {"X","Y","W","H"};

  /** the <I>x</I>-coordinate of the box */
  public int x;

  /** the <I>y</I>-coordinate of the box */
  public int y;

  /** the width of the box */
  public int w;

  /** the height of the box */
  public int h;

  /** Constructs a new MBox with default values. */
  public MBox () {
    this(0,0,100,100);
  }

  /** Constructs a new MBox at the given (x,y) location with the given width and height.
      @param x the <I>x</I>-coordinate of the box
      @param y the <I>y</I>-coordinate of the box
      @param w the width of the box
      @param h the height of the box
   */
  public MBox (int x, int y, int w, int h) {
    setBounds (x,y,w,h);
  }

  /** Constructs a new MBox with the same position and dimensions as the given rectangle.
      @param rect The rectangle defining the dimensions of the box.
      @since NeXtMidas 2.1.0
   */
  public MBox (Rectangle rect) {
    this(rect.x, rect.y, rect.width, rect.height);
  }

  /** Constructs a new MBox with the same position and dimensions as the given MBox.
      @param pos The MBox defining the dimensions of the box.
      @since NeXtMidas 2.1.0
   */
  public MBox (MBox pos) {
    this(pos.x, pos.y, pos.w, pos.h);
  }

  /** Changes the size and location of this box.
      @param x the new <I>x</I>-coordinate for this box
      @param y the new <I>y</I>-coordinate for this box
      @param w the new width for this box
      @param h the new height for this box
   */
  public void setBounds (int x, int y, int w, int h) {
    this.x=x; this.y=y; this.w=w; this.h=h;
  }

  /** Changes the size and location of this box.
      @param pos the MBox containing the new location and size of this box
   */
  public void setBounds (MBox pos) {
    setBounds(pos.x, pos.y, pos.w, pos.h);
  }

  /** Changes the size and location of this box.
      @param rect the rectangle containing the new location and size of this box
      @since NeXtMidas 2.1.0
   */
  public void setBounds (Rectangle rect) {
    setBounds(rect.x, rect.y, rect.width, rect.height);
  }

  /** Changes the size (but not location) of this box.
      @param w The new width for this box (must be positive).
      @param h The new height for this box (must be positive).
      @since NeXtMidas 2.1.0
   */
  public void setSize (int w, int h) {
    this.w = w; this.h = h;
  }

  /** Changes the size (but not location) of this box.
      @param d The new dimensions of the box (must have positive values).
      @since NeXtMidas 2.1.0
   */
  public void setSize (java.awt.Dimension d) {
    setSize(d.width, d.height);
  }

  /** Changes the location (but not size) of this box.
      @param x the new <I>x</I>-coordinate for this box.
      @param y the new <I>y</I>-coordinate for this box.
      @since NeXtMidas 2.1.0
   */
  public void setLocation (int x, int y) {
    this.x=x; this.y=y;
  }

  /** Changes the location (but not size) of this box.
      @param p The new location dimensions of the box.
      @since NeXtMidas 2.1.0
   */
  public void setLocation (java.awt.Point p) {
    setLocation(p.x, p.y);
  }

  /** If the width and/or height values of this MBox have negative values,
      change them to positive values.
   */
  public void reform () {
    if (w<0) { w=-w; x=x-w; }
    if (h<0) { h=-h; y=y-h; }
  }

  /** Determine whether the point at the given (x,y) location is within the bounds of this box.
      @param px the <I>x</I>-coordinate of the point to find
      @param py the <I>y</I>-coordinate of the point to find
      @return true if the point is located in the bounds of this box
   */
  public boolean within (int px, int py) {
    return ( px>=x && px<=x+w && py>=y && py<=y+h );
  }

  /** Translates this <code>MBox</code> the indicated distance, to the right
      along the x coordinate axis, and  downward along the y coordinate axis.
      @param px the distance to move this <code>MBox</code> along the x axis
      @param py the distance to move this <code>MBox</code> along the y axis
   */
  public void translate (int px, int py) {
    x+=px; y+=py;
  }

  /** Creates a java.awt.Rectangle using the bounds of this box.
      @return a new Rectangle
   */
  public Rectangle toRectangle() {
    return new Rectangle(x,y,w,h);
  }


//  Added by J. Czechowski, but it does not meet the equals contract
//  Contract: 1) It is symmetric: for any non-null reference values x and y, x.equals(y)
//            should return true if and only if y.equals(x) returns true.
//            RectangleWHXY.equals(MBoxWHXY) is false
//            MBoxWHXY.equals(RectangleWHXY) would according to the code be true,
//            2) Equal objects must have equal hash codes (the hashCode method for
//            Rectangle is in the Rectangle2D class.  This class inherits from Object.
//  @Override
//  public boolean equals (Object o) {
//    return (o instanceof Rectangle) ? equals((Rectangle)o) : super.equals(o);
//  }

  /** Check if specified Rectangle has the same position and dimensions as this box.
   *  @param r rectangle to compare with this box
   *  @return true if specified rectangle has the same position and dimensions as this box
   */
  public boolean equals (Rectangle r) {
    return (r!=null) && (r.width==w) && (r.height==h) && (r.x==x) && (r.y==y);
  }

  /** Creates a description of the box that is useful for debugging purposes. */
  @Override
  public String toString () {
    return "Box("+x+","+y+","+w+","+h+")";
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////
  // Methods implementing the Keyable interface
  //////////////////////////////////////////////////////////////////////////////////////////////////
  @Override
  public String[] getKeys() { return keys; }

  @Override
  public Object setKey (String key, Object value) {
         if (key.equals("X")) x = Convert.o2l(value);
    else if (key.equals("Y")) y = Convert.o2l(value);
    else if (key.equals("W")) w = Convert.o2l(value);
    else if (key.equals("H")) h = Convert.o2l(value);
    else return null;
    return value;
  }

  @Override
  public Object getKey (String key) {
    if (key.equals("X")) return Convert.l2o(x);
    if (key.equals("Y")) return Convert.l2o(y);
    if (key.equals("W")) return Convert.l2o(w);
    if (key.equals("H")) return Convert.l2o(h);
    return null;
  }
}
