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

// $Id: WidgetRegistry.java,v 1.18 2003/03/06 00:40:39 moltmans Exp $
// Time-stamp: <2001-04-24 10:47:09 moltmans>
package rec.sys;


///////////////////////////////////////////////////////////////////////////////
//
// FILE:	WidgetRegistry.java
//
// AUTHOR:	Manoj D. Muzumdar
//		archon@ai.mit.edu
//
// DATE:	June, 1998
//
// COPYRIGHT:	Massachusetts Institute of Technology
//		All rights reserved.
//
// PURPOSE:	Drawing recognition
//
// HISTORY:     September, 1998
//              MDM
//              modified to account for new transition structure
//
///////////////////////////////////////////////////////////////////////////////

import java.util.*;
import rec.util.*;

/**
 * All necessary changes are made to the Registry before any
 *     notifications are made.
 */
public class WidgetRegistry implements Registry
{
  public WidgetRegistry()
  {
    m_Table = new Hashtable();
  }

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

  public int getSize()
  {
    return( m_Table.size() );
  }

  public void add( Widget w )
  {
    doAdd( w );
  }

  /**
   * This is a perverse way to make the UndoableWidgetRegistry
   * work right.  (So multiple Transitions are not recorded for the
   * same basic event.)
   * I know it sucks, and I apologize. -lw
   */
  protected void doAdd( Widget w )
  {
    // LUKE: This comes up if Registerble widgets register for
    // stuff before they are added to surface.  Causes an assert
    // error here.
    //    Assert.check( !contains( w ),
    //		  "duplicate Widget Id: " + w.getId() );
    if ( contains( w ) )
      return;

    //    Console.writeln( "WR: Adding " + w );
    m_Table.put( w.getId(), new TableEntry( w ) );
  }

  public void add( Collection ws )
  {
    doAdd( ws );
  }
  
  private void doAdd( Collection ws )
  {
    Iterator e = ws.iterator();
    while( e.hasNext() )
       doAdd( (Widget)e.next() );
  }

  public void remove( Widget w )
  {
    // LUKE: This comes up if Registerable widgets register for
    // stuff before they are added to surface.  Causes an assert
    // error here.
    if ( !contains( w ) )
      return;

    TableEntry te = lookup( w );

    doRemove( w );

    notifyRemoved( w, te.m_Registry );
  }


  public void doRemove( Widget w )
  {
    Assert.check( w != null, "attempting to remove null widget" );
    Assert.check( contains( w ), "widget " + w + " not found" );

    //        Console.writeln( "WR: Removing " + w );

    TableEntry te = lookup( w );
    m_Table.remove( w.getId() );

    // unregister anything the widget was listening to.

    //        Console.writeln( "WR: Removing2 " + w );
    Assert.check( te != null );
    if ( te.listener() )
      {
	Assert.check( te.m_Registered != null );
	Iterator e = te.m_Registered.iterator();
	while( e.hasNext() )
	  {
	    Widget targ = (Widget)e.next();

	    //widget registered with self.
	    if ( targ.equals( w ) )
	      continue;

	    TableEntry tte = lookup( targ );
	    tte.m_Registry.remove( w );
	  }
      }

    //        Console.writeln( "WR: Removing3 " + w );
    Assert.check( te.m_Registry != null );
    Iterator e = te.m_Registry.iterator();
    while( e.hasNext() )
      {
	Registerable re = (Registerable)e.next();
	if ( re instanceof rec.sys.Widget )
	  {
	    if ( re.equals( w ) )
	      continue;

	    TableEntry tte = lookup( (Widget)re );
	    tte.m_Registered.remove( w );
	  }
      }
  }

  /**
   * @param ow  The old widget being replaced.
   * @param new The new world-order widget.
   */
  public void replace( Widget ow, Widget nw )
  {
    replace( ow, nw, true );
  }

  public void replace( Widget ow, Widget nw, boolean notify )
  {
    HashSet notify_set = null;
    if ( notify )
      notify_set = (HashSet) lookup(ow).m_Registry.clone();

    // do the replace
    doReplace( ow, nw );

    if ( notify )
      {
	// notify folks
	notifyReplaced( ow, nw, notify_set );
      }
  }

  protected void doReplace( Widget ow, Widget nw )
  {
    Assert.check( contains( ow ), "old widget " + ow + " not found" );

    //    Console.writeln( "WR: Replacing " + ow + " with " + nw );
    TableEntry nte = lookup( ow );
    TableEntry ote = lookup( ow );
    
    // put in new widget
    m_Table.put( nw.getId(), new TableEntry( nw ) );

    // make all things registered with old widget registered
    // with new widget.
    Iterator e = ote.m_Registry.iterator();
    while( e.hasNext() )
      {
	Registerable re = (Registerable)e.next();
	doUnregister( re, ow );
	doRegister( re, nw );
	e = ote.m_Registry.iterator();
      }
    
    // re-register anything the old-widget was listening to with
    // the new widget
    if ( nw instanceof rec.sys.Registerable &&ote.listener() )
      {
        e = ote.m_Registered.iterator();
	while( e.hasNext() )
	  {
	    Widget targ = (Widget)e.next();
	    doUnregister( (Registerable)ow, targ );
	    doRegister( (Registerable)nw, targ );

	    // fix the Iterator since things got clocked.
	    // need iterators!
	    e = ote.m_Registered.iterator();
	  }
      }

    // remove old table 
    m_Table.remove( ow.getId() );
  }


  /**
   * @param ows  The SET of old widget being replaced.
   * @param new The new world-order widget.
   */
  public void replace( HashSet ows, Widget nw )
  {
    replace( ows, nw, true );
  }

  public void replace( HashSet ows, Widget nw, boolean do_notify )
  {
    HashSet notify = new HashSet();

    Iterator e = ows.iterator();
    while( e.hasNext() )
      {
	Widget ow = (Widget)e.next();
	notify.addAll( (Collection)( lookup(ow).m_Registry.clone() ) );

	// do the replace
	doReplace( ow, nw );
      }

    if ( do_notify )
      {
	// notify folks
	notifyReplaced( ows, nw, notify );
      }
  }


  /**
   * @param ow  The old widget being replaced.
   * @param nws The SET of new world-order widgets.
   */
  public void replace( Widget ow, HashSet nws )
  {
    replace( ow, nws, true );
  }

  public void replace( Widget ow, Collection nws, boolean do_notify )
  {
    Assert.check( contains( ow ), "old widget " + ow + " not found" );

    //    Console.writeln( "WR: Replacing " + ow + " with " + nw );
    TableEntry nte = lookup( ow );

    HashSet notify = (HashSet) nte.m_Registry.clone();

    TableEntry ote = lookup( ow );
    
    // put in all the new widgets
    doAdd( nws );

    // make all things registered with old widget registered
    // with new widget.
    Iterator e = ote.m_Registry.iterator();
    while( e.hasNext() )
      {
	Registerable re = (Registerable)e.next();
	doUnregister( re, ow );

	doRegister( re, nws );

	// since Iterators change when tinkered with (via the unregister),
	// get new Iterator.
	e = ote.m_Registry.iterator();
      }
    
    // re-register anything the old-widget was listening to with
    // the new widget
    if ( ote.listener() )
      {
        e = ote.m_Registered.iterator();
	while( e.hasNext() )
	  {
	    Widget targ = (Widget)e.next();
	    doUnregister( (Registerable)ow, targ );

	    Iterator e2 = nws.iterator();
	    while( e2.hasNext() )
	      {
		Widget nw = (Widget)e2.next();
		if ( nw instanceof rec.sys.Registerable )
		  doRegister( (Registerable)nw, targ );
	      }

	    // fix the Iterator since things got clocked.
	    // need iterators!
	    e = ote.m_Registered.iterator();
	  }
      }

    // remove old table 
    m_Table.remove( ow.getId() );


    if ( do_notify )
      {
	// notify folks
	notifyReplaced( ow, nws, notify );
      }
  }

  public void replace( Collection ows, Collection nws )
  {
    replace( ows, nws, true );
  }

  public void replace( Collection ows, Collection nws, boolean do_notify )
  {

    Assert.check( ows != null, "OWS is null" );
    Assert.check( nws != null, "NWS is null" );
    // put in all the new widgets
    //doAdd( nws );

    Iterator it = ows.iterator();
    while ( it.hasNext() ) {
      Widget ow = (Widget)it.next();
      
//        Console.writeln( "WR: Replacing " + ow + " with " + nw );
//        TableEntry nte = lookup( ow );

//        Assert.check( nte != null, "NTE is null" );
      
//        HashSet notify = (HashSet) nte.m_Registry.clone();

//        TableEntry ote = lookup( ow );

//        Assert.check( ote != null, "OTE is null" );

//        // make all things registered with old widget registered
//        // with new widget.
//        Iterator e = ote.m_Registry.iterator();
//        while( e.hasNext() )
//        {
//  	Registerable re = (Registerable)e.next();
//  	doUnregister( re, ow );
	
//  	doRegister( re, nws );

//  	// since Iterators change when tinkered with (via the unregister),
//  	// get new Iterator.
//  	e = ote.m_Registry.iterator();
//        }
    
//        // re-register anything the old-widget was listening to with
//        // the new widget
//        if ( ote.listener() )
//        {
//          e = ote.m_Registered.iterator();
//  	while( e.hasNext() )
//  	{
//  	  Widget targ = (Widget)e.next();
//  	  doUnregister( (Registerable)ow, targ );

//  	  Iterator e2 = nws.iterator();
//  	  while( e2.hasNext() )
//  	  {
//  	    Widget nw = (Widget)e2.next();
//  	    if ( nw instanceof rec.sys.Registerable )
//  	      doRegister( (Registerable)nw, targ );
//  	  }

//  	  // fix the Iterator since things got clocked.
//  	  // need iterators!
//  	  e = ote.m_Registered.iterator();
//  	}
//        }

//        // remove old table 
//        m_Table.remove( ow.getId() );
    
      
//        if ( do_notify )
//        {
//  	// notify folks
//  	notifyReplaced( ow, nws, notify );
//        }
      replace( ow, nws, do_notify );
    }

  }
  
  public void alter( Widget w, AlterTransition t )
  {
    alter( w, t, true );
  }

  public void alter( Widget w, AlterTransition t, boolean notify )
  {
    Assert.check( contains( w ), "widget not found" + w );

    if ( notify )
      {
	TableEntry te = lookup( w );
	notifyAltered( w, t, te.m_Registry );
      }
  }

    
  public Widget get( Id id )
  {
    TableEntry te = lookup( id );

    Assert.check( te != null, "widget not found" );

    return( te.m_Widget );
  }

  public boolean contains( Widget w )
  {
    return contains( w.getId() );
  }

  public boolean contains( Id id )
  {
    return( m_Table.containsKey( id ) );
  }

  protected TableEntry lookup( Widget w )
  {
    return (TableEntry)m_Table.get( w.getId() );
  }

  protected TableEntry lookup( Id id )
  {
    return (TableEntry)m_Table.get( id );
  }

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


  public boolean isRegistered( Registerable r, Widget w )
  {
    Assert.check( w != null, "widget null in isRegistered" );
    Assert.check( r != null, "Registerable object null in isRegistered" );

    TableEntry te = lookup( w );
    
    Assert.check( te != null, "widget " + w + " not in register." );
    Assert.check( te.m_Registry != null, "widget " + w + " doesn't have registry." );
    return te.m_Registry.contains( r );
  }

  /**
   * Registerable registers for which Id to get notifications
   *     about.
   */
  public void register( Registerable r, Widget w )
  {
    doRegister( r, w );
  }


  protected void doRegister( Registerable r, Widget w )  
  {
    //    Assert.check( contains( w ), "Widget not found." );
    if ( !contains( w ) )
      add( w );

    Console.writeln( "" + r + " regisistering interest in " + w );
    TableEntry te = lookup( w );

    te.m_Registry.add( r );

    // now we check to see if r is a widget, and if it is we register
    // that it registered
    if ( r instanceof rec.sys.Widget )
      {
	te = lookup( (Widget)r );
	if ( te == null )
	  {
	    //	    Console.writeln( "Adding registerable " + r + " to WR." );
	    doAdd( (Widget)r );

	    te = lookup( (Widget)r );
	  }
	
	// add widget as thing r is interested in.
	if ( !te.m_Registered.contains( w ) )
	  te.m_Registered.add( w );
      }
  }


  /**
   * Register in a whole heap of widgets.
   */
  public void register( Registerable r, Collection ws )
  {
    doRegister( r, ws );
  }

  private void doRegister( Registerable r, Collection ws )
  {
    Iterator e = ws.iterator();
    while( e.hasNext() )
      doRegister( r, (Widget)e.next() );
  }
  

  /**
   * The registerable unregisters for widget it does not want
   *     to get notifications about anymore.
   */
  public void unregister( Registerable r, Widget w )
  {
    doUnregister( r, w );
  }

  private void doUnregister( Registerable r, Widget w )
  {
    // LUKE: This comes up if Registerable widgets register for
    // stuff before they are added to surface.  Causes an assert
    // error here.
    //    Assert.check( contains( w ), "Widget " + w + " not found." );
    Console.writeln( "" + r + " losing interest in " + w );

    if ( ! contains( w ) )
      return;

    TableEntry te = lookup( w );

    te.m_Registry.remove( r );

    // if r is a widget, remove the note of its interest in its own
    // entry
    if ( r instanceof rec.sys.Widget )
      {
	te = (TableEntry)lookup( (Widget)r );
	if ( te != null )
	  te.m_Registered.remove( w );
      }
  }

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

  private void notifyRemoved( Widget w, HashSet v )
  {
    Console.writeln( "notifying removal " + v + " to " + v.size() + " folks.");
    Iterator e = v.iterator();
    while( e.hasNext() ) {
      Registerable r = (Registerable)e.next();
      Console.writeln( "note removal to- " + r );
      Console.indent();
      r.removed( w );
      Console.outdent();
    }
  }

  private void notifyReplaced( Widget ow, Widget nw, HashSet v )
  {
    Iterator e = v.iterator();
    while( e.hasNext() ) {
      Registerable r = (Registerable)e.next();
      r.replaced( ow, nw );
    }
  }


  private void notifyReplaced( Widget ow, Collection nws, HashSet v )
  {
    Iterator e = v.iterator();
    while( e.hasNext() ) {
      Registerable r = (Registerable)e.next();
      //Console.writeln( "note replace to- " + r );
      //Console.indent();
      r.replaced( ow, nws );
      //Console.outdent();
    }
  }


  private void notifyReplaced( Collection ows, Widget nw, HashSet v )
  {
    Iterator e = ows.iterator();
    while( e.hasNext() ) {
      notifyReplaced( (Widget)e.next(), nw, v );
    }
  }

  private void notifyAltered( Widget w, AlterTransition t, HashSet v )
  {
    Iterator e = ((HashSet)v.clone()).iterator();
    while( e.hasNext() ) {
      Registerable r = (Registerable)e.next();
      Console.writeln( "note alter " + w.getId() + " to- " + r );
      Console.indent();
      r.altered( w, t );
      Console.outdent();
      Console.writeln( "alter end" );
    }
  }

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

  protected class TableEntry implements Cloneable
  {

    public TableEntry( Widget w, HashSet registry )
    {
      m_Widget = w;

      m_Registry = (HashSet) registry.clone();

      m_Registered = new HashSet();
    }

    public TableEntry( Widget w )
    {
      m_Widget = w;

      m_Registry = new HashSet();
      m_Registered = new HashSet();
    }

    public Object clone( )
    {
      try {
      return super.clone();
      }
      catch ( CloneNotSupportedException e ) 
      {
	e.printStackTrace();
	Assert.error("Error Cloning a TableEntry");
      }

      return null;
    }

    public Widget m_Widget;
    public HashSet m_Registry;

    // this holds widgets which are registered as listeners.  When they
    // get removed, replaced, their registration needs to be updated.
    public HashSet m_Registered;

    
    public boolean listener()
    {
      return ( m_Registered.size() > 0 );
    }

  }

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

  private Hashtable m_Table;
  
}

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