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

// $Id: FastMoveTargetRecognizer.java,v 1.7 2003/03/06 00:40:48 moltmans Exp $
// Time-stamp: <2000-03-11 18:54:46 moltmans>
package rec.core.recognizers;


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

public class FastMoveTargetRecognizer extends FastRecognizer
  implements EditModeRecognizer
{
  // the thing being moved (null if none)
  protected WSelection m_Selection;
  protected VisibleWidget movie;
  protected double m_Offset_X;
  protected double m_Offset_Y;
  
  public FastMoveTargetRecognizer( )
  {
  }

  // edit mode recognizer interface:
  public void setModeArg( Object arg )
  {
    Assert.check( arg instanceof WSelection,
		  "Fast Move Target Recognizer can only be used with selections as targets" );
    m_Selection = (WSelection)arg;
  }


    public WSelection getSelection(){
	return m_Selection;
    }
  
  /**
   * 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, rec.sys.Squiggle st, boolean ending )
  {
    if( movie == null ) {

      GPoint p = st.getFirstPoint();
	
      GeometryCalculator calc = new GeometryCalculator( s );
      GeoFunction gf = new GeoFunction() {
	public boolean check( VisibleWidget vw ) {
	  return m_Selection.isInSelection( vw );
	}
      };
      
      movie = calc.findSelectedWithCheck( p, gf );

      if( movie == null ) {
	//Console.writeln( "nothing selected." );
	return( DONT_WANT );
      }
      else {
	//Console.writeln( vw + " selected-- going to take it" );
	// get the offset so we don't always move from the middle
	Console.writeln( movie + " is starting centered at " +
			 movie.getLocation().getCenter() );
	m_Offset_X = st.getFirstPoint().x -
	  movie.getLocation().getCenter().x;
	m_Offset_Y = st.getFirstPoint().y -
	  movie.getLocation().getCenter().y;
      }
    }
    
    if( st.getNumberOfPoints() > 5 )
      return( WANT );
    else
      return( UNSURE );
  }

  public void reset()
  {
    movie = null;
  }

  public boolean recognize( Surface s, rec.sys.Squiggle st, boolean ending )
  {
    GPoint prevLocation = movie.getLocation().getCenter();

    GPoint actual_move = new GPoint( st.getLastPoint().x - m_Offset_X,
				     st.getLastPoint().y - m_Offset_Y );
    
    double x_dif = actual_move.x - prevLocation.x;
    double y_dif = actual_move.y - prevLocation.y;
    
    Iterator it = m_Selection.getMovables().iterator();
    while ( it.hasNext() ) {
      Object o = it.next();
      if ( o instanceof VisibleWidget ) {
	GPoint old_pos = ((VisibleWidget)o).getLocation().getCenter();
	GPoint new_pos = new GPoint( old_pos.x + x_dif, old_pos.y + y_dif );
	DoTransition t = new MoveTransition( s,
					     (VisibleWidget)o,
					     new_pos );
	t.doDo();
	s.addTransition( t );
	//Console.writeln( "moving " + o );
      }
    }  
    
    // i think that if we decide that the fast move is not what we
    // want to do we are going to have to undo lots of stuff from the
    // transition recorder anyway.
    // i guess we are just assume the "old-style" recognition for fast
    // recognition so make the squiggle die.
    if ( ending ) {
      // break the broken ones
      Collection broken = m_Selection.getBroken();
      DoTransition dt_kill = new DoTransition( s ) {
	DoTransition exp_trans;
	
	public void doDo()
	{
	  Iterator dead_it = m_Selection.getBroken().iterator();
	  while ( dead_it.hasNext() ) {
	    ((Widget)dead_it.next()).die();
	  }
	  exp_trans = m_Surface.expunge();
	}
	public void doUndo()
	{
	  exp_trans.doUndo();
	  Iterator alive_it = m_Selection.getBroken().iterator();
	  while (alive_it.hasNext()) {
	    ((Widget)alive_it.next()).revive();
	  }
	}
	public String getAction()
	{
	  return "gets the dead ones out of the widget pool";
	}
      };
      DoTransition dt_sever = new SeverTransition( s, broken );

      DoTransition dt_break = new DoTransition( s ) {
	Collection trans = new HashSet();

	public void doDo()
	{
	  Collection brokenLitters = new HashSet();
	  Iterator it_lit = m_Selection.getLittersToBreak().iterator();
	  while ( it_lit.hasNext() ) {
	    Widget w = (Widget)it_lit.next();
	    brokenLitters.add( w );
	    brokenLitters.addAll( w.getLitter() );
	  }
	  it_lit = brokenLitters.iterator();
	  while ( it_lit.hasNext() ) {
	    DoTransition donext = new RemoveLitterTransition( m_Surface, (Widget)it_lit.next() );
	    donext.doDo();
	    trans.add( donext );
	  }
	}
	public void doUndo()
	{
	  Iterator it_lit = trans.iterator();
	  while ( it_lit.hasNext() ) {
	    ((DoTransition)it_lit.next()).doUndo();
	  }
	}
	public String getAction()
	{
	  return "kills the litters of transitions";
	}
      };
      
      DoTransition dt = new DieTransition( s, st );
      st.addToSuitcase( dt_kill );
      st.addToSuitcase( dt_sever );
      st.addToSuitcase( dt_break );
      st.addToSuitcase( dt );
    }
      
    // now we have nothing in the widget pool from the action.
    
    return( true );
  }

  public Iterator getOutputTypes()
  {
    return ( new Vector() ).iterator();
  }
}

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

