// (c) MIT 2003.  All rights reserved.

////////////////////////////////////////////////////////////////////////////////
//
// FILE:	WPart.java
//
// AUTHOR:	Manoj D. Muzumdar
//		archon@ai.mit.edu
//
// DATE:	Jul 98
//
// COPYRIGHT:	Massachusetts Institute of Technology
//		All rights reserved.
//
// PURPOSE:     Representation for a mechanical engineering part
//
// HISTORY:     Jul 98  archon  created
//
//              Dec 98  archon
//              Integrated new rec.geo changes.
//
//              Feb 99  archon
//              Integrated with the part library.
//
//              Apr 99  archon
//              Added operating (center) point support.
//
////////////////////////////////////////////////////////////////////////////////

package rec.meche;

import java.lang.reflect.*;
import java.awt.*;
import rec.util.*;
import rec.geo.*;
import rec.sys.*;

/**
 * A WPart is a representation of a mechanical engineering part.
 *
 * The components are created in create() and WPart wraps them up;
 *
 * WPart should be overridden to use (create() and clone() need to be
 *   overriden)
 */
public abstract class WPart extends VisibleWidget
{
  /**
   * @param center  The center point.
   * @param angle   The angle in radians.
   * @param size    The size in average line lengths.
   * @param data    Additional data to create the part.
   */
  public WPart( GPoint center, double angle, double size, Cloneable data )
  {
    m_Center = null;

    GShape[] ws = create( size, data );

    Assert.check( ws != null && ws.length >= 1,
		  "parts must have at least one component" );

    m_Components = ws;
    m_Size = size;

    if( data == null ) {
      m_Data = null;
    }
    else {
      Class c = data.getClass();

      Method m = null;

      try {
	m = c.getMethod( "clone", null );
	m_Data = (Cloneable)m.invoke( data, null );
      }
      catch( Exception e ) {
	Assert.fatal( "data was not cloneable" );
      }
    }

    if( m_Center == null )
      m_Center = getLocation().getCenter();

    move( center );
    rotateTo( angle );

    //    setColor( Color.blue );
  }

  //////////////////////////////////////////////////////////////////////////////

  protected abstract GShape[] create( double size, Cloneable data );

  //////////////////////////////////////////////////////////////////////////////

  protected void setCenter( GPoint p )
  {
    m_Center = p;
  }

  //////////////////////////////////////////////////////////////////////////////

  public GShape[] getComponents()
  {
    return( m_Components );
  }

  public GPoint getCenter()
  {
    return( m_Center );
  }

  public double getAngle()
  {
    return( m_Angle );
  }

  public Cloneable getData()
  {
    return( m_Data );
  }

  //////////////////////////////////////////////////////////////////////////////

  public double size()
  {
    return( m_Size );
  }

  public void resize( double scaleFactor )
  {
    // do nothing for now!
  }

  //////////////////////////////////////////////////////////////////////////////

  public void move( GPoint p )
  {
    for( int i = 0; i < m_Components.length; ++i ) {
      m_Components[i].translate( m_Center, p );
    }

    m_Center = new GPoint( p );
  }

  /**
   * @param p      The point of rotation.
   * @param angle  THe angle in radians.
   */
  public void rotate( GPoint p, double angle )
  {
    for( int i = 0; i < m_Components.length; ++i ) {
      m_Components[i].rotate( p, Radian.reduce( angle ) );
    }

    m_Angle = Radian.reduce( m_Angle + angle );
  }

  /**
   * @param angle  The angle in radians.
   */
  public void rotateTo( double angle )
  {
    // It is important to get the center first from the bounds
    //      because the bounds will change after each
    //      component is rotated.

    for( int i = 0; i < m_Components.length; ++i ) {
      m_Components[i].rotate( m_Center,
			      Radian.reduce( angle - Math.PI / 2.0 ) );
    }

    m_Angle = Radian.reduce( angle );
  }

  //////////////////////////////////////////////////////////////////////////////

  public Location getLocation()
  {
    return( new Location( getBounds() ) );
  }

  public Rectangle getBounds()
  {
    Rectangle r = m_Components[0].getBounds();

    // Top left corner point.
    int tlx = r.x;
    int tly = r.y;

    // Bottom right corner point.
    int brx = r.x + r.width;
    int bry = r.y + r.height;

    for( int i = 1; i < m_Components.length; ++i ) {

      r = m_Components[i].getBounds();

      tlx = Math.min( tlx, r.x );
      tly = Math.min( tly, r.y );
      brx = Math.max( brx, r.x + r.width );
      bry = Math.max( bry, r.y + r.height );
    }

    return( new Rectangle( tlx, tly, brx - tlx, bry - tly ) );
  }

  //////////////////////////////////////////////////////////////////////////////

  public void draw( Graphics g )
  {
    for( int i = 0; i < m_Components.length; ++i ) {
      m_Components[i].display( g );
    }
  }

  //////////////////////////////////////////////////////////////////////////////

  public boolean includes( GPoint p, double offset )
  {
    for( int i = 0; i < m_Components.length; ++i ) {
      if( m_Components[i].includes( p, offset ) )
	return( true );
    }

    return( false );
  }

  public boolean contains( GPoint p )
  {
    for( int i = 0; i < m_Components.length; ++i ) {
      if( m_Components[i].contains( p ) )
	return( true );
    }

    return( false );
  }


  //////////////////////////////////////////////////////////////////////////////

  public abstract Object clone();

  //////////////////////////////////////////////////////////////////////////////

  private GShape[] m_Components;
  private GPoint m_Center;
  private double m_Angle;
  private double m_Size;
  private Cloneable m_Data;
}

////////////////////////////////////////////////////////////////////////////////
