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

package rec.house;


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

public class WallConnector extends Tracker
{
  public static final int BREAK = 20;

  WingGraph wg;

  public WallConnector()
  {
  }		  

  
  public Iterator getInputTypes()
  {
    Vector v = new Vector();
    v.addElement( "rec.house.Wall" );
    return v.iterator();
  }

  public boolean recognize( Surface s, Widget wid )
  {
    //    Console.writeln( "WC Begin Recognize" );
    super.recognize( s, wid );

    if ( wg == null )
      wg = new UndoableWingGraph( s );

    if ( wg.hasEdge( wid ) )
      {
	if ( hasMoved( (Wall) wid ) )
	  return moveWall( (Wall)wid );
	else
	  return false;
      }
    else 
      {
	return addWall( (Wall)wid );
      }
  }


  public boolean moveWall( Wall w )
  {
    Console.writeln( "WC moving wall: " + w );
    removeWall( w );
    
    return addWall( w );
  }


  public void removeWall( Wall w )
  {
    Console.writeln( "removing wall/edge: " + w );

    // first remove it from us
    forget( w );

    Object[] ends = wg.getEnds( w );
    wg.removeEdge( w );
    checkEnds( ends );
  }


  public boolean addWall( Wall w )
  {
    track( w );

    doAdd( w );

    Object[] ends = wg.getEnds( w );
    checkEnds( ends );

    return true;
  }


  public boolean hasMoved( Wall w )
  {
    GPoint h = (GPoint) wg.getHead( w );
    GPoint t = (GPoint) wg.getTail( w );
    GPoint wh = w.getHead();
    GPoint wt = w.getTail();

    Console.writeln( "heads n tails: " + wh + ", " + wt + "   /   " 
		     + h + ", " + t );

    // check to see if the endpoints from WingGraph are close to
    // the endpoints of wall.  If they are, return false--we have
    // not moved.
    if ( Geometry.distance( h, wh ) < BREAK )
      {
	return !( Geometry.distance( t, wt ) < BREAK );
      }
    else
      {
	if ( Geometry.distance( h, wt ) < BREAK )
	  {
	    return !( Geometry.distance( t, wh ) < BREAK );
	  }
	else
	  return true;
      }
  }

  public boolean doAdd( Wall w )
  {
    Console.writeln( "Adding wall/edge " + w );
    GPoint p_head = null;
    double d_head = BREAK;
    GPoint p_tail = null;
    double d_tail = BREAK;

    Iterator e = wg.points();
    while( e.hasNext() )
      {
	GPoint p = (GPoint)e.next();
	
	double tmp = Geometry.distance( p, w.getHead() );
	if ( tmp < d_head )
	  { p_head = p; d_head = tmp; }
	
	tmp = Geometry.distance( p, w.getTail() );
	if ( tmp < d_tail ) 
	  { p_tail = p; d_tail = tmp; }
      }
    
    if ( p_head == null )
      {
	p_head = (GPoint) w.getHead().clone();
	Console.writeln( "New wingpoint " + p_head );
	wg.addPoint( p_head );
      }
    else
      Console.writeln( "Linking wingpoint " + p_head );

    if ( p_tail == null )
      {
	p_tail = (GPoint)w.getTail().clone();
	Console.writeln( "New wingpoint " + p_tail );
	wg.addPoint( p_tail );
      }
    else
      Console.writeln( "Linking wingpoint " + p_tail );
    
    wg.addEdge( w, p_head, p_tail );

    return true;
  }


  private boolean checkEnds( Object[] ends )
  {
    boolean result = false;
    for ( int i = 0; i < 2; i++ )
      {
	int s = wg.degree( ends[i] );
	if ( s == 0 )
	  {
	    Console.writeln( "removing point " + ends[i] );
	    wg.removePoint( ends[i] );
	  }
	else if ( s == 2 )
	  {
	    result = merge( (GPoint) ends[i] );
	  }
	else
	  Console.writeln( "there are " + s + " ends here." );
      }

    return result;
  }

  boolean merge( GPoint merge_point )
  {
    Console.writeln( "** Doing a merge check!" );

    // check for merge.
    Object[] ed = wg.getEdges( merge_point );
    Wall w1 = (Wall)ed[0];
    Wall w2 = (Wall)ed[1];
    
    Console.writeln( "merge at "+merge_point + " with " + w1 + " and " + w2 );
    
    Console.writeln( "angs=" +
		     w1.getLine().getAngle() + " " + w2.getLine().getAngle() );
    double v = ( 2 * Math.PI + w1.getLine().getAngle() - w2.getLine().getAngle() );

    v *= 360 / ( 2 * Math.PI ); // convert to degrees

    //shift over so we look at -30 to 30 degrees
    int iv = ( 30 + (int)v ) % 360;
    Console.writeln( "v = " + v + ", " + iv );

    if ( iv < 60 )
      {
	HashSet s = wg.neighbors( merge_point );
	Iterator it = s.iterator();
	GPoint p1 = (GPoint)it.next();
	GPoint p2 = (GPoint)it.next();
	
	Console.writeln( "MERGIE MERGIE!" );
	// first unregister for wall notices
	surface.unregister( this, w1 );
	surface.unregister( this, w2 );

	HashSet set = new HashSet();
	set.add( w1 );
	set.add( w2 );

	// fix the WingGraph
	wg.removeEdge( w1 );
	wg.removeEdge( w2 );
	wg.removePoint( merge_point );

	Wall new_w = new Wall( surface, p1, p2 );
	Console.writeln( "Going to merge" );
	surface.replaceWidget( set, new_w, true );

	// now add the new wall and update the WingGraph!
	// FUNNY STUFF HERE--I HAVEN'T THOUGHT IT THROUGH.
	addWall( new_w );

	Console.writeln( "END MERGIE!" );
	return true;
      }
    Console.writeln( "merge failed." );
    return false;
  }

  public void removed( Widget widget )
  {
    Console.writeln( "WC Begin Removing " + widget );

    super.removed( widget );

    removeWall( (Wall)widget );
  }


  public void replaced( Widget ow, Collection nws )
  {
    Console.writeln( "Multireplace on " + ow + " to " + nws 
		     + "--kind of unsure about implementation." );

    super.replaced( ow, nws );
    wg.removeEdge( ow );
    Iterator it = nws.iterator();
    while( it.hasNext() )
      doAdd( (Wall)it.next() );
  }



  public void replaced( Widget ow, Widget nw )
  {
    Console.writeln( "WC Begin: Hearing about Single WallConn. replace" );

    super.replaced( ow, nw );
    wg.replaceEdge( ow, nw );
  }  


  public void altered( Widget widget, Transition t )
  {
    super.altered( widget, t );

    // if wall has moved, then update our stuff
    if ( hasMoved( (Wall)widget ) )
      moveWall( (Wall)widget );
  }



} // end WallConnector


