/*
 *This library is free software; you can redistribute it and/or
 *modify it under the terms of the GNU Lesser General Public
 *License as published by the Free Software Foundation; either
 *version 2.1 of the License, or (at your option) any later version.
 *
 *This library is distributed in the hope that it will be useful,
 *but WITHOUT ANY WARRANTY; without even the implied warranty of
 *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *Lesser General Public License for more details.
 *
 *You should have received a copy of the GNU Lesser General Public
 *License along with this library; if not, write to the Free Software
 *Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/* 
 * This software was written by Julian Selley (j.selley@man.ac.uk)
 * whilst at the University of Manchester as a Research Assistant. 
 *
 * The initial code base is copyright by the University of Manchester. 
 * Modifications to the initial code base are copyright of their 
 * respective authors, or their employers as appropriate. Authorship 
 * of the modifications may be determined from the ChangeLog placed 
 * at the end of this file
 */

package uk.ac.man.bioinf.sequence.geom; // Package name inserted by JPack


/**
 * SequenceAlignmentRectangle.java
 *
 * Defines a rectangular region in a sequence alignment. NB: objects
 * of this class perform no checks as to the validity of the region
 * defined. 
 *
 * Created: Mon Mar 27 12:41:18 2000
 *
 * @author J Selley
 * @version $Id: SequenceAlignmentRectangle.java,v 1.6 2001/04/11 17:04:43 lord Exp $
 */

public class SequenceAlignmentRectangle implements SequenceAlignmentShape, Comparable
{
  private SequenceAlignmentPoint location;
  private SequenceAlignmentDimension size;

  public SequenceAlignmentRectangle()
  {
    this( 1, 1, 1, 1 );
  }
  
  public SequenceAlignmentRectangle(int x, int y, int width, int height) 
  {
    setBounds(x, y, width, height);
  }
  
  public SequenceAlignmentRectangle(SequenceAlignmentRectangle rect) 
  {
    setBounds(rect);
  }
  
  public SequenceAlignmentRectangle
    (int x, int y, SequenceAlignmentDimension dim) 
  {
    setBounds(x, y, dim.getWidth(), dim.getHeight());
  }
  
  public SequenceAlignmentRectangle
    (SequenceAlignmentPoint point, int width, int height) 
  {
    setBounds(point.getX(), point.getY(), width, height);
  }
  
  /**
   * Determines whether a point is contained in this region.
   *
   * @param x the position in the sequence
   * @param y the sequence index in the alignment
   * @return whether a point is contained in this region
   */
  public boolean contains(int x, int y) 
  {
    /*
     * if x and y are greater than the corresponding location to this
     * rectangle, but less than the corresponding location +
     * dimension, then the point is contained within this rectangle.
     * The - 1's are because a SARhas a size of at least 1
     */
    if (((x >= this.location.getX()) && (y >= this.location.getY())) &&
	((x <= (this.location.getX() + this.size.getWidth() - 1 )) &&
	 (y <= (this.location.getY() + this.size.getHeight() - 1 )))){
      return true;
    }
    else{
      return false;
    }
  }
  
  /**
   * Determines whether a point is contained in this region.
   *
   * @param point the point
   * @return whether a point is contained in this region
   */
  public boolean contains(SequenceAlignmentPoint point) 
  {
    /*
     * Delegates to the method contains(int, int) to allow easy update
     * of the procedure
     */
    return contains(point.getX(), point.getY());
  }
  
  /**
   * Compares two SARectangles for equality (ie: whether they have the
   * same dimension and location).
   *
   * @param obj the SARectangle for comparison
   * @return the equality of the objects
   */
  public boolean equals(Object obj) 
  {
    /*
     * if the object is an instance of SARectangle, and the point and
     * dimensions are equal, then the objects are said to be
     * equivelent.
     */
    if ((obj instanceof SequenceAlignmentRectangle) && 
	((((SequenceAlignmentRectangle)obj).getLocation().equals(this.location)) && 
	 (((SequenceAlignmentRectangle)obj).getSize().equals(this.size))))
      return true;
    else
      return false;
  }
  
  /**
   * Compares another rectangle to this one. Comparison is done by
   * comparing the location point. If this is equal then the width,
   * then the height are compared. Throws a class cast if obj is
   * not a SequenceAlignmentRectangle
   */
  public int compareTo( Object obj )
  {
    SequenceAlignmentRectangle rect = (SequenceAlignmentRectangle)obj;
    
    // get rid of the equals case first. 
    if( equals( rect ) ) return 0;

    // now compare locations
    int comp = location.compareTo( rect.location );
    
    if( comp != 0 ) return comp;
    
    // so locations are the same, compare width, and then height
    if( size.getWidth() <  rect.size.getWidth() ){
      return -1;
    } else if( size.getHeight() < rect.size.getHeight() ){
      return -1;
    }
    
    return 1;
  }
  
    
  /**
   * Sets the boundry defined by this region.
   *
   * @param x the position in the sequence
   * @param y the sequence index in the alignment
   * @param width the width of the SARectangle
   * @param height the height of the SARectangle
   */
  public void setBounds(int x, int y, int width, int height) 
  {
    this.location = new SequenceAlignmentPoint(x, y);
    this.size = new SequenceAlignmentDimension(width, height);
  }
  
  /**
   * Sets the boundry defined by this region.
   *
   * @param point the origin location of the new SARectangle
   * @param dim the size of the new SARectangle
   */
  public void setBounds
    (SequenceAlignmentPoint point, SequenceAlignmentDimension dim) 
  {
    /*
     * Delegates action to setBounds(int, int, int, int) to enable
     * easy update of procedures
     */
    setBounds(point.getX(), point.getY(), dim.getWidth(), dim.getHeight());
  }

  /**
   * Sets the boundry defined by this region.
   *
   * @param rect the rectangle
   */
  public void setBounds(SequenceAlignmentRectangle rect) 
  {
    /*
     * Delegates action to setBounds(int, int, int, int) to enable
     * easy update of procedures
     */
    setBounds(rect.getLocation().getX(), rect.getLocation().getY(), 
	      rect.getSize().getWidth(), rect.getSize().getHeight());
  }
  
  /**
   * Returns the rectangle which defines the boundries of the region
   * defined by this object.
   * @see SequenceAlignmentShape
   *
   * @return the rectangle
   */
  public SequenceAlignmentRectangle getBounds()     // from SequenceAlignmentShape
  {
    return new SequenceAlignmentRectangle(this);
  }
  
  /**
   * Sets the progom location of the rectangle. X corresponds to the
   * position in the sequence, and Y corresponds to the index of the
   * sequence in the alignment.
   *
   * @param x the new X location of the rectangle
   * @param y the new Y location of the rectangle
   */
  public void setLocation(int x, int y) 
  {
    this.location = new SequenceAlignmentPoint(x, y);
  }
  
  /**
   * Sets the origin location of the rectangle.
   *
   * @param point the new location
   */
  public void setLocation(SequenceAlignmentPoint point) 
  {
    /*
     * Delegates action to setLocation(int, int) so that if
     * modifications to code are necessary, they can be made in one
     * place.
     */
    setLocation(point.getX(), point.getY());
  }
  
  /**
   * Returns the location of the rectangle
   *
   * @return the location
   */
  public SequenceAlignmentPoint getLocation() 
  {
    return new SequenceAlignmentPoint(this.location);
  }
  
  /**
   * Sets the size of the rectangle.
   *
   * @param width the width of the new rectangle
   * @param height the height of the new rectangle
   */
  public void setSize(int width, int height) 
  {
    this.size = new SequenceAlignmentDimension(width, height);
  }
  
  /**
   * Sets the size of the rectangle.
   *
   * @param dim the new dimension
   */
  public void setSize(SequenceAlignmentDimension dim) 
  {
    /*
     * Delegates action to setSize(int, int) so that if modifications
     * to code are necessary, they can be made in one place.
     */
    setSize(dim.getWidth(), dim.getHeight());
  }
  
  /**
   * Returns the size of the rectangle.
   *
   * @return the dimensions of the rectangle
   */
  public SequenceAlignmentDimension getSize() 
  {
    return new SequenceAlignmentDimension(this.size);
  }

  public int getX()
  {
    return location.getX();
  }
  
  public int getY()
  {
    return location.getY();
  }
  
  public int getHeight()
  {
    return size.getHeight();
  }
  
  public int getWidth()
  {
    return size.getWidth();
  }
  
  public void add( SequenceAlignmentPoint point )
  {
    add( point.getX(), point.getY() );
  }
  
  public void add( int newx, int newy )
  {
    // this code is culled from java.awt.Rectangle
    // (PENDING:- PL) Does this not suggest that all of the geom
    // classes should be re-written to simply wrap the awt geom
    // classes? Probably.
    int x = location.getX();
    int y = location.getY();
    int width  = size.getWidth();
    int height = size.getHeight();

    // the some what counter intuitive + 1's here are because the
    // minimum size of a Rectangle is 1, 1...
    int x1 = Math.min( x, newx );
    int x2 = Math.max( x + width, newx + 1);
    int y1 = Math.min( y, newy );
    int y2 = Math.max( y + height, newy + 1 );
    
    setLocation( x1, y1 );
    setSize( (x2 - x1), (y2 - y1) );
  }
  
  public String toString()
  {
    return super.toString() + " Location [" + location.getX() + "x" + location.getY() +
      "+" + size.getWidth() + "+" + size.getHeight() + "]";
  }
} // SequenceAlignmentRectangle



/*
 * ChangeLog
 * $Log: SequenceAlignmentRectangle.java,v $
 * Revision 1.6  2001/04/11 17:04:43  lord
 * Added License agreements to all code
 *
 * Revision 1.5  2001/01/23 17:58:00  lord
 * Support for comparable interface
 *
 * Revision 1.4  2001/01/19 19:50:23  lord
 * This class now uses exclusive numbers. This makes it behave more
 * intuitively. At least in some circumstances. In others it makes it
 * less so.
 *
 * Revision 1.3  2000/06/13 11:03:24  lord
 * Added getX, getY methods
 *
 * Revision 1.2  2000/04/13 15:37:19  lord
 * Added "add" method. Fiddled with "contains" method
 *
 * Revision 1.1  2000/03/27 13:29:48  jns
 * o initial coding of a SA rectangle which may be used to define a region in
 * a MSA.
 *
 */


