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

////////////////////////////////////////////////////////////////////////////////
//
// FILE:	FrameRecognizer.java
//
// AUTHOR:	Manoj D. Muzumdar
//		archon@ai.mit.edu
//
// DATE:	Mar 99
//
// COPYRIGHT:	Massachusetts Institute of Technology
//		All rights reserved.
//
// PURPOSE:	Mechanical engineering drawing recognition
//
// HISTORY:     Mar 99  archon  created
//
////////////////////////////////////////////////////////////////////////////////

package rec.meche;

import java.awt.*;
import java.util.*;
import rec.util.*;
import rec.geo.*;
import rec.sys.*;
import rec.core.widgets.*;

public class FrameRecognizer extends Recognizer
{
  public static WMFrame isFrame( WLine l1, WLine l2, WLine l3,
				 WLine l4, WLine l5 )
  {
    GSegment s1 = new GSegment( l1.getHead(), l1.getTail() );
    GSegment s2 = new GSegment( l2.getHead(), l2.getTail() );
    GSegment s3 = new GSegment( l3.getHead(), l3.getTail() );
    GSegment s4 = new GSegment( l4.getHead(), l4.getTail() );
    GSegment s5 = new GSegment( l5.getHead(), l5.getTail() );

    WMFrame frame = null;

    if( (frame = findFrame( s1, s2, s3, s4, s5 )) != null )
      return( frame );

    if( (frame = findFrame( s2, s1, s3, s4, s5 )) != null )
      return( frame );

    if( (frame = findFrame( s3, s2, s1, s4, s5 )) != null )
      return( frame );

    if( (frame = findFrame( s4, s2, s3, s1, s5 )) != null )
      return( frame );

    if( (frame = findFrame( s5, s2, s3, s4, s1 )) != null )
      return( frame );

    return( null );
  }

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

  private static WMFrame findFrame( GSegment base,
				    GSegment s1, GSegment s2,
				    GSegment s3, GSegment s4 )
  {
    // Check if segments are near the base.

    if( !( Estimator.isNear( base, s1 )
	   && Estimator.isNear( base, s2 )
	   && Estimator.isNear( base, s3 )
	   && Estimator.isNear( base, s4 ) ) )
      return( null );

    // Check if the sizes are correct.

    if( !( Estimator.isSameSize( s1.size(), s2.size() )
	   && Estimator.isSameSize( s2.size(), s3.size() )
	   && Estimator.isSameSize( s3.size(), s4.size() )
	   && base.size()
	   > (s1.size() + s2.size() + s3.size() + s4.size()) / 2.0
	   && base.size()
	   <  (s1.size() + s2.size() + s3.size() + s4.size()) * 4.0 ) )
      return( null );

    // Now check out the angles.

    GPoint i1 = null;

    if( (i1 = base.getSafeIntersection( s1 )) == null )
      return( null );

    GPoint i2 = null;

    if( (i2 = base.getSafeIntersection( s2 )) == null )
      return( null );

    GPoint i3 = null;

    if( (i3 = base.getSafeIntersection( s3 )) == null )
      return( null );

    GPoint i4 = null;

    if( (i4 = base.getSafeIntersection( s4 )) == null )
      return( null );

    double ref = Geometry.angle( i1, s1.getMidpoint() );

    //Console.writeln( "ref: " + ref );

    double angle = Geometry.angle( i2, s2.getMidpoint() );

    //Console.writeln( "angle: " + angle );

    if( !Estimator.isSameAngle( ref, angle ) )
      return( null );

    angle = Geometry.angle( i3, s3.getMidpoint() );

    //Console.writeln( "angle: " + angle );

    if( !Estimator.isSameAngle( ref, angle ) )
      return( null );

    angle = Geometry.angle( i4, s4.getMidpoint() );

    //Console.writeln( "angle: " + angle );

    if( !Estimator.isSameAngle( ref, angle ) )
      return( null );

    //Console.writeln( "create the frame!" );

    // Should be a frame!

    ref = Radian.reduce( Geometry.angle( base.getHeadPoint(),
					 s1.getMidpoint() )
                           - Geometry.angle( base.getHeadPoint(),
					     base.getTailPoint() ) );
    angle = Radian.reduce( Geometry.angle( base.getTailPoint(),
					   s1.getMidpoint() )
                             - Geometry.angle( base.getTailPoint(),
					       base.getHeadPoint() ) );

    // Righthand case (so go from head to tail).

    if( ref > Math.PI && angle < Math.PI ) {
      return( new WMFrame( base.getMidpoint(),
			   Geometry.angle( base.getHeadPoint(),
					   base.getTailPoint() )
			     + Math.PI / 2.0,
			   base.size(),
			   null ) );
    }

    // Lefthand case (so go from tail to head).

    if( ref < Math.PI && angle > Math.PI ) {
      return( new WMFrame( base.getMidpoint(),
			   Geometry.angle( base.getTailPoint(),
					   base.getHeadPoint() )
			     + Math.PI / 2.0,
			   base.size(),
			   null ) );
    }

    // Something messed up!

    return( null );
  }

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

  public FrameRecognizer()
  {
  }

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

  public boolean recognize( Surface s, Widget w )
  {
    WLine l = (WLine)w;

    GeometryCalculator gc = new GeometryCalculator( s );

    Vector vect = new Vector( gc.getVisibleWidgets() );

    if( vect.size() < 5 )
      return( false );

    for( int i = 0; i < vect.size(); ++i ) {

      Widget vw1 = (Widget)vect.elementAt( i );

      if( vw1 instanceof WLine && !vw1.equals( l ) ) {

	for( int j = 0; j < vect.size(); ++j ) {

	  Widget vw2 = (Widget)vect.elementAt( j );

	  if( vw2 instanceof WLine && !vw2.equals( l )
	      && !vw2.equals( vw1 ) ) {

	    for( int k = 0; k < vect.size(); ++k ) {

	      Widget vw3 = (Widget)vect.elementAt( k );

	      if( vw3 instanceof WLine && !vw3.equals( l )
		  && !vw3.equals( vw1 ) && !vw3.equals( vw2 ) ) {

		for( int q = 0; q < vect.size(); ++q ) {

		  Widget vw4 = (Widget)vect.elementAt( q );

		  if( vw4 instanceof WLine && !vw4.equals( l )
		      && !vw4.equals( vw1 ) && !vw4.equals( vw2 )
		      && !vw4.equals( vw3 ) ) {

		    WMFrame part = isFrame( l, (WLine)vw1, (WLine)vw2,
					    (WLine)vw3, (WLine)vw4 );

		    if( part != null ) {

		      s.removeWidget( l );
		      s.removeWidget( vw1 );
		      s.removeWidget( vw2 );
		      s.removeWidget( vw3 );
		      s.removeWidget( vw4 );

		      s.addWidget( part );

		      return( true );
		    }
		  }
		}
	      }
	    }
	  }
	}
      }
    }

    return( false );
  }

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

  public Iterator getInputTypes()
  {
    Vector v = new Vector();

    v.addElement( "rec.core.widgets.WLine" );

    return( v.iterator() );
  }

  public Iterator getOutputTypes()
  {
    Vector v = new Vector();

    v.addElement( "rec.meche.WMFrame" );

    return( v.iterator() );
  }
}

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