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

package rec.fsm;

////////////////////////////////////////////////////////////////////////////////
//
// FILE:	MoveEdgeRecognizer.java
//
// AUTHOR:      Luke Weisman
//              luke@ai.mit.edu
//              Manoj D. Muzumdar
//		archon@ai.mit.edu
//
// DATE:	June, 1998
//
// COPYRIGHT:	Massachusetts Institute of Technology
//		All rights reserved.
//
///////////////////////////////////////////////////////////////////////////////


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

public class MoveEdgeRecognizer extends FastRecognizer
{
  // the thing being moved (null if none)
  Edge movie;
  boolean head_hit;

  public MoveEdgeRecognizer()
  {
  }

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

  /**
   * This is called with every point added to the stroke.  If the 
   * rec returns WANT it gets full control of all added points,
   * UNSURE makes no comment, and DONT_WANT stops all calls.
   * 
   * DONT_WANT is not absolutely needed.  UNSURE works as well.
   */
  public int want( Surface s, Squiggle st, boolean ending )
  {
    if( movie == null ) {

      final GPoint p = st.getFirstPoint();
	
      GeometryCalculator calc = new GeometryCalculator( s );
      GeoFunction gf = new GeoFunction() {
	public boolean check( VisibleWidget vw )
	  {
	    if ( vw instanceof rec.fsm.Edge )
	      {
		Edge e = (Edge)vw;
		double len = 10; //e.length() / 5;
		double d = Geometry.distance( e.getHead(), p );
		if ( d < len )
		  return true;

		d =  Geometry.distance( e.getTail(), p );
		if ( d < len )
		  return true;
	      }
	    
	    return false;	    
	  }
      };

      movie = (Edge)calc.findWidget( gf );
      if( movie == null ) {
	//Console.writeln( "nothing selected." );
	return( DONT_WANT );
      }
      else 
	{
	  head_hit = ( Geometry.distance( movie.getHead(), p ) 
		       < Geometry.distance( movie.getTail(), p ) );
	  //Console.writeln( movie + " endpoint selected-- going to take it" );
	}
    } // end movie==null
    
    if( st.getNumberOfPoints() > 5 )
      return( WANT );
    else
      return( UNSURE );
  }

  public void reset()
  {
    movie = null;
    head_hit = false;
  }

  public boolean recognize( Surface s, Squiggle st, boolean ending )
  {
    GPoint dest = st.getLastPoint();

    // this makes a transition which moves the endpoint _to_ dest
    Transition t = new MoveEdgeTransition( s, movie, head_hit, dest );

    // disconnect any node.
    if ( (head_hit && movie.headLinked() ) 
	 || ( !head_hit && movie.tailLinked() ) )
      Aligner.disconnect( s, movie, head_hit );

    // do the move and get the inverse for the undo.
    t = t.undo();

    s.alterWidget( movie, (AlterTransition)t, ending );

    return( true );
  }

  public Iterator getOutputTypes()
  {
    return( null );
  }



  /**
   * This holds all the undo info for a node and edge hooking up.
   *
   */
  class MoveEdgeTransition extends AlterTransition {
    
    GPoint old_loc;
    GPoint loc;
    boolean head;

    public MoveEdgeTransition( Surface surf,
			       Edge edge,
			       boolean head,
			       GPoint dest )
    {
      super( surf, edge );

      this.loc = dest;
      this.head = head;
      this.old_loc = head ? edge.getHead() : edge.getTail();
    }

    public void doUndo( )
    {
      Edge edge = (Edge)getTargetWidget();

      if ( head )
	{
	  edge.setHead( loc );
	}
      else
	{
	  edge.setTail( loc );
	}
    }
    
    public Transition makeRedoTransition( )
    {
      return new MoveEdgeTransition( getSurface(), (Edge) getTargetWidget(),
				     head, old_loc );
    }

    // an extension of luke's little hack.  this is not the way to do
    // this, but it's what you get when you try to simplify something
    // complicated without structure breaking down.
    public void doDo()
    {
      getRedoTransition().undo();
    }

    public String getAction()
    {
      return "hooks up a node and an edge";
    }
    
    public String name()
    {
      return "moveEdge";
    }
  }


} // end MoveEdgeRecognizer

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