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

package rec.fsm;

/////////////////////////////////////////////////////////////////////////////
//
// FILE:	Node.java
//
// AUTHOR:      Luke Weisman
//		luke@ai.mit.edu
//
//              Adapted from code written by Luke Weisman
//              luke@ai.mit.edu
//
// DATE:	June, 1998
//
// COPYRIGHT:	Massachusetts Institute of Technology
//		All rights reserved.
//
// PURPOSE:	Drawing recognition
//
///////////////////////////////////////////////////////////////////////////////


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


public class Node extends WCircle implements Registerable, FSM
{
  public static int ACCEPT_SPACING = 6;

  public static double NORMAL_RADIUS = 30.0;

  static {
    Colors.setDefaultColor( "node", "stroke" );
  }

  // list of edges attached to node.
  Vector edges = new Vector();

  String name = null;

  boolean accept = false;

  transient Surface surf = null;

  public Node( GPoint center, double radius, Surface surf )
  {
    super( center, radius );
    
    this.surf = surf;
  }

  public Node( GPoint center, Surface surf )
  {
    this( center, NORMAL_RADIUS, surf );
  }

  public Node( WCircle c, Surface surf )
  {
    super( c.getCenter(), c.getRadius() );
    
    this.surf = surf;
  }

  /**
   * Creates the best fitting circle for the Rectangle.
   */
  public Node( Rectangle r, Surface surf )
  {
    super( r );
    
    this.surf = surf;
  }



  //  parent takes this
  public void draw( Graphics g )
  {
    double radius = getRadius();
    GPoint center = getCenter();

    Color cc = null;
    if ( getColor() != null )
      cc = getColor();
    else
      cc =  Colors.getColor( "node" );

    //    cc = new Color( cc.getRed(), cc.getGreen(), cc.getBlue(), 100 );
    g.setColor( cc );

    int cx = (int)(center.x - radius );
    int cy = (int)(center.y - radius );
    int wh =(int)( 2.0 * radius);

    g.drawOval( cx, cy, wh, wh );
    //    g.drawOval( cx + 2, cy + 2, wh - 4, wh - 4 );

    if ( isAccept() )
      {
	//	System.err.println( "Accept!" );
	g.drawOval( cx - ACCEPT_SPACING, cy - ACCEPT_SPACING,
		    wh + 2 * ACCEPT_SPACING, wh + 2 * ACCEPT_SPACING );
      }

    GPoint c = getCenter();
    int x = c.getIntX(), y = c.getIntY();

    Font of = g.getFont();
    AffineTransform af = AffineTransform.getScaleInstance( 1.5, 1.5 );
    Font nf = of.deriveFont( Font.BOLD, af );

    g.setFont( nf );
    g.setColor( Colors.getColor( "text" ) );
    FontMetrics fm = g.getFontMetrics();
    int off = fm.getDescent();
    //    int off = 0;

    String nm = null;
    if ( name != null )
      nm = name;
    else
      nm = this.toString();

    int wd = fm.stringWidth( nm ) / 2;
    
    g.drawString( nm, x - wd, y + off );
    
    g.setFont( of );
  }

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

  public Object clone()
  {
    //Console.writeln( "In Node.clone()" );

    Node n = new Node( (WCircle)this, surf );

    n.edges = (Vector)edges.clone();

    if ( name != null )
      n.name = name.toString();

    return n;
  }



  public void setName( String the_name )
  {
    final String new_name = the_name;
    final String old_name = name;

    AlterTransition tr =  new FlipAlterTransition( surf, this ) {

      public void doUndo( )
	{
	  name = new_name;
	  //Console.writeln( "Changing the name for the node." );
	}

      public void doDo( )
	{
	  ((Node)getTargetWidget()).name = old_name;
	  //Console.writeln( "Reset the name to old value." );
	}

      public String name()
	{
	  return "node-name";
	}
    };

    tr = (AlterTransition)tr.undo();
    surf.alterWidget( this, tr );
  }


  public String getName( )
  {
    return name;
  }


  public void setAccept( boolean new_accept )
  {
    //System.err.println( "Setting accept for node." );
    if ( this.accept == new_accept )
      return;

    AlterTransition tr =  new FlipAlterTransition( surf, this ) {
      public void doUndo( )
	{
	  accept = !accept;
	  //Console.writeln( "Changing accept for the node." );
	}

      public void doDo( )
	{
	  accept = !accept;
	  //Console.writeln( "Reset accept to old value." );
	}

      public String name()
	{
	  return "node-accept";
	}
    };

    tr = (AlterTransition)tr.undo();
    surf.alterWidget( this, tr );
  }


  public boolean isAccept( )
  {
    return accept;
  }


  public String toString( )
  {
    return super.toString() + "-" + getName()
      + "/" + edges.size();
  }


  /**
   * This does not tell the edge about it.
   */
  public void addEdge( Edge e )
  {
    Assert.check( e.isConnected( this ),
		  "adding edge to node w/o edge connection." );

    if ( !edges.contains( e ) )
      {
	//Console.writeln( "Node linking up to " + e );
	doEdgeSetManipulation( e, true );
	surf.register( this, e );
      }
    else
      Console.writeln( "Node already linked up to " + e );
  }

  public void removeEdge( Edge e )
  {
    doEdgeSetManipulation( e, false );
    surf.unregister( this, e );
  }


  private void doEdgeSetManipulation( Edge ed, boolean add )
  {
    final Edge e = ed;
    Transition at = new FlipAlterTransition( surf, e ) {
      public void doUndo( )
	{
	  edges.removeElement( e );
	}
      public void doDo( )
	{
	  edges.addElement( e );
	}
      public String name() 
	{
	  return "edgeset";
	}
    };
    
    if ( ! add )
      at = at.undo();
    else
      at.dodo();

    surf.addTransition( at );
  }

  // the register stuff
  public void removed( Widget w )
  {
    //Console.writeln( this.toString() + " caught a " + w + " dying on me." );
    removeEdge( (Edge)w );
  }

  /**
   * Called when ow is replaced by nw.
   */
  public void replaced( Widget ow, Widget nw )
  {
    //Console.writeln( toString() + " sees " + ow + " turning into a " + nw );
    removeEdge( (Edge)ow );
    addEdge( (Edge)nw );
  }

  /***
   * Called when w has been altered.
   *
   * @param w  The Widget after it has been altered.
   * @param t  The Transition that affected that Widget.
   */
  public void altered( Widget w, Transition t )
  {
    //Console.writeln( toString() + " sees altered " + w + " " + t );
    // caught edge moving (rotation?)  So refix endpoints and force edge
    // back to where it should be.
    Edge e = (Edge)w;
    e.snap();
  }
  

  public Vector getEdges( )
  {
    return (Vector)edges.clone();
  }

  public Vector getEdgesTo( Node n )
  {
    Vector v = new Vector();

    int s = edges.size();

    //special case of self-edges
    if ( this.equals( n ) )
      {
	for ( int i = 0; i < s; i++ )
	  {
	    Edge ed = (Edge)edges.elementAt(i);
	    
	    // if both ends connected to me, it is a self-edge
	    if ( this.equals( ed.getNode(true) ) 
		 && this.equals(ed.getNode(false)) )
	      v.addElement( ed );
	  }
      }
    else
      {
	//the rest
	for ( int i = 0; i < s; i++ )
	  if ( ( (Edge)edges.elementAt(i) ).isConnected(n) )
	    v.addElement( edges.elementAt(i) );
      }

    return v;
  }

  public boolean isConnected( Node n )
  {
    Iterator e = edges.iterator();
    while( e.hasNext() )
      {
	if ( ((Edge)e.next()).isConnected( n ) )
	  return true;
      }

    return false;
  }

}

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