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

package rec.fsm;

///////////////////////////////////////////////////////////////////////////////
//
// FILE:	ArrowRecognizer.java
//
// AUTHOR:      Luke Weisman
//              luke@ai.mit.edu
//
// DATE:	June, 1998
//
// COPYRIGHT:	Massachusetts Institute of Technology
//		All rights reserved.
//
// PURPOSE:	Drawing recognition system
//
///////////////////////////////////////////////////////////////////////////////


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


public class ArrowRecognizer extends Recognizer
{

  public ArrowRecognizer()
  {
  }

  /////////////////////////////////////////////////////////////////////////////
  
  /**
   * Each new stroke generated gets put into the pipe here.
   *     It will remove and add objects to the 'out' target
   *     as it deems fit.
   */
  public boolean recognize( Surface s, Widget w )
  {
    GPoint p = isArrowhead( (Squiggle)w );

    if ( p != null )
      {
	Console.writeln( "Arrowhead online" );

	boolean resp =  attachArrowhead( s, p, (Squiggle)w );
	if ( resp )
	  s.removeWidget( w );

	return resp;
      }

    return false;
  }

  /**
   * Return a point which is the tip of the arrowhead, or null
   * if the stroke is not an arrowhead.
   */
  static GPoint isArrowhead( Squiggle s )
    {
      int size = s.getNumberOfPoints();
      if ( size < 3 )
	return null;


      /*
	for ( int i = 1; i < size - 2; i++ )
	if ( LineRecognizer.isLine( s.getSub( 0, i ) )
	     && LineRecognizer.isLine( s.getSub( i, size-1 ) ) )
	  return s.getPointAt( i );
	  */

	/*
      if ( LineRecognizer.isLine( s.getSub( 0, size / 2 ) )
	   || LineRecognizer.isLine( s.getSub( size/2, size-1 ) ) )
	return s.getPointAt( size/2 );
	*/

      return s.getPointAt( size / 2 );

      //      return null;
    }



  boolean attachArrowhead( Surface b, GPoint p, Squiggle s )
  {
    Edge best = null;
    boolean head = true;
    double b_d, t;

    Iterator e = b.getWidgetsIterator();
    double size = s.getLength() / 2; // the length of arrowhead side
    size *= 2;  // we are looking for a line who is at least 2 x as long
    // as arrowhead side

    b_d = 10000;
    
    // find most likely Edge
    while ( e.hasNext() )
      {
	Object o = e.next();
	if ( !( o instanceof Edge ) )
	  continue;

	Edge l = (Edge)o;
	if ( l.getLength() > size ) // make sure line is bigger than arrowhead
	  {
	    if (  ( t = Geometry.distance( l.getHead(), p )) < b_d )
	      {
		best = l;
		b_d = t;
		head = true;
	      }
	    
	    if ( ( t = Geometry.distance( l.getTail(), p )) < b_d )
	      {
		best = l;
		b_d = t;
		head = false;
	      }
	  }
      }

    if ( ( best != null ) && ( b_d < 50 ) )  // size / 4 ) )
      //    if ( best != null )
      {
	// found a Edge
	best.addArrowhead( head );
	final boolean on_head = head;

	AlterTransition un = new FlipAlterTransition( b, best ) {
	  public void doUndo()
	    {
	      ((Edge)getTargetWidget()).removeArrowhead( on_head );
	    }
	  public void doDo()
	    {
	      ((Edge)getTargetWidget()).addArrowhead( on_head );
	    }
	  public String name()
	    {
	      return "arrowhead";
	    }
	};
	  
	b.alterWidget( best, un );

	return true;
      }
    else
      return false;
  }


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

    v.addElement( "rec.sys.Squiggle" );

    return( v.iterator() );
  }

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

    v.addElement( "rec.fsm.Edge" );

    return( v.iterator() );
  }

}

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