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

package rec.fsm;


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


public abstract class Aligner extends Recognizer
{

  // this saves all the modifications done by the aligner in
  // a single go-around.  (So all the edge or node moves would
  // be stored in a single unit here.)
  //  MultiTransition trans;


  /**
   * Connect an edge and node if the edge has an empty endpoint
   * in the node's perimeter.  Return true if this is done.
   */
  boolean snapEdgeToNode( Surface surf, Edge edge, Node node )
  {
    boolean changed = false;

    changed = doSnap( surf, edge, node, true );
    changed = doSnap( surf, edge, node, false ) || changed;

    return changed;
  }


  /**
   * Snap a node to an edge.  head is what end of the edge to look at.
   * It checks to see if the node and edge are close first.
   * Return true if snapped to Edge.
   */
  public boolean doSnap( Surface surf, Edge edge, Node node, boolean head )
  {
    if ( !edge.linked( head )
	 && (Geometry.distance( edge.getPoint(head), node.getCenter() )
	     < node.getRadius() + 10 ) )
      {
	//Console.writeln( "linking h=" + head + ": " + edge );
	
	// do the change
	edge.setNode( node, head );

	// make the undo for further use
	AlterTransition tr = makeUndoNodeSet( surf, edge, node, head );
	//	trans.add( tr );
	surf.alterWidget( edge, tr );

	return true;
      }

    return false;
  }




  public boolean doSnap( Surface surf, Edge edge, Node node )
  {
    boolean c = doSnap( surf, edge, node, true );
    c = doSnap( surf, edge, node, false ) || c;

    return c;
  }

  public static void disconnect( Surface surf, Edge edge, Node node )
  {
    boolean head = edge.isHead( node );
    if ( !head )
      Assert.check( edge.isTail( node ), "null in disconnect" );
    disconnect( surf, edge, head );
  }

  public static void disconnect( Surface surf, Edge edge, boolean head )
  {
    Node node = edge.getNode( head );
    Assert.check( node != null, "Node needs to be non-null" );

    // make transition for the node --> null connection
    AlterTransition tr = new AlignerTransition( surf,
						edge,
						node,
						null,
						head );

    // get its reverse.
    tr = (AlignerTransition) tr.getRedoTransition();

    // run it and get old transition back.
    tr = (AlignerTransition) tr.undo();

    surf.alterWidget( edge, tr );
  }

  /**
   * make a transition which sets/unsets the node head
   * stuff.
   */
  private AlterTransition makeUndoNodeSet( Surface surf,
					   Edge e,
					   Node nn,
					   boolean h )
  {
    return new AlignerTransition( surf, e, null, nn, h );
  }





  public boolean forceSnap( Surface surf, Edge edge, Node node, boolean head )
  {
    if ( !edge.linked( head ) )
      {
	//Console.writeln( "linking h=" + head + ": " + edge );
	
	// do the change
	edge.setNode( node, head );

	// make the undo for further use
	AlterTransition tr = makeUndoNodeSet( surf, edge, node, head );
	//	trans.add( tr );
	surf.alterWidget( edge, tr );

	return true;
      }

    return false;
  }




}


  /**
   * This holds all the undo info for a node and edge hooking up.
   */
  class AlignerTransition extends AlterTransition {

    Node node;
    Node old_node;
    boolean head;

    public AlignerTransition( Surface surf,
			      Edge edge,
			      Node old_node,
			      Node node,
			      boolean head )
    {
      super( surf, edge );

      this.node = node;
      this.old_node = old_node;
      this.head = head;
    }

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

      edge.setNode( old_node, head );
    }
    
    public Transition makeRedoTransition( )
    {
      return new AlignerTransition( getSurface(), (Edge) getTargetWidget(),
				    node, old_node, head );
    }
    
    // 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 "aligns some nodes???";
    }
    
    public String name()
    {
      return "node-aligner";
    }
  }


