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

// $Id: WidgetPool.java,v 1.11 2003/03/06 00:40:39 moltmans Exp $
// Time-stamp: <2001-04-03 19:21:23 moltmans>
package rec.sys;


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

/**
 * Ordered list semantics.  Widgets are maintained in a list by when they
 * were drawn
 */
public class WidgetPool
{
  protected Hashtable m_Table;
  protected TreeSet m_SortedSet;

  public WidgetPool()
  {
    java.util.Comparator comp = new WidgetTimeComparator();
    m_Table = new Hashtable();
    m_SortedSet = new TreeSet( comp );
  }

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

  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 )
  {
    if ( contains( w ) )
      return;

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

  public void add( Collection ws )
  {
    doAdd( ws );
  }
  
  private void doAdd( Collection ws )
  {
    Iterator e = ws.iterator();
    while( e.hasNext() ) {
      Object o = e.next();
      //Console.writeln( o.toString() );
      doAdd( (Widget)o );
    }
  }

  public void remove( Widget w )
  {
    m_Table.remove( w.getId() );
    m_SortedSet.remove( w );
  }

  public Iterator iterator()
  {
    return m_SortedSet.iterator();
  }

  public Collection getWidgets()
  {
    return m_SortedSet;
  }

  /** 
   * Remove the dead widgets from the pool and return them
   */
  public Collection expunge()
  {
    Collection dead_ones = new Vector();
    Iterator it = m_Table.values().iterator();
    
    while ( it.hasNext() ) {
      Widget w = (Widget)it.next();
      if ( !w.alive() ) {
	it.remove();
	m_SortedSet.remove( w );
	dead_ones.add( w );
      }
    }

    return dead_ones;
  }

  public void replace( Widget ow, Widget nw )
  {
    HashSet ows = new HashSet();
    ows.add( ow );
    HashSet nws = new HashSet();
    nws.add( nw );

    replace( ows, nws );
  }

  public void replace( Widget ow, HashSet nws )
  {
    HashSet ows = new HashSet();
    ows.add( ow );
    
    replace( ows, nws );
  }

  public void replace( HashSet ows, Widget nw )
  {
    HashSet nws = new HashSet();
    nws.add( nw );

    replace( ows, nws );
  }


  public void replace( Set ows, Set nws )
  {
    // set the parents of the new widgets and add them to pool
    Iterator it = nws.iterator();
    while ( it.hasNext() ) {
      Widget w = (Widget)it.next();

      // set the parents
      w.setParents( ows );

      // add the new widgets to the pool
      m_Table.put( w.getId(), w );
      m_SortedSet.add( w );
    }

    // set the children of the old widgets
    it = ows.iterator();
    while ( it.hasNext() ) {
      Widget w = (Widget)it.next();
      w.addChildren( nws );
    }
  }
  
  /**
   * @return a transition which undoes the effect of the
   *    replace (if the doUndo method of it is called, that
   *    is).
   *    Perhaps this undo stuff in the middle of such a fine
   *    data structure is out of place, and if so, perhaps a
   *    UndoableWidgetPool class is in order?
   */
  public DoTransition replaceAndGetTransition( Surface surf,
					       Set ows, Set nws ) 
  {
    // make a cup to put all the transitions in.
    GroupTransition gt = new GroupTransition( surf );
    
    // set the parents of the new widgets and add them to pool
    Iterator it = nws.iterator();
    while ( it.hasNext() ) {
      Widget w = (Widget)it.next();

      // set the parents
      w.setParents( ows );

      // add the new widgets to the pool
      Console.writeln( "Adding " + w + " to widget pool" );
      m_Table.put( w.getId(), w );
      m_SortedSet.add( w );

      // add the links between child and parent (and vice versa)
      gt.addTransition( new DisinheritTransition( surf, w, ows ) );


      gt.addTransition( new PoolAddTransition( surf, w ) );
      
    }

    // set the children of the old widgets
    it = ows.iterator();
    while ( it.hasNext() ) {
      Widget w = (Widget)it.next();
      w.addChildren( nws );
    }

    return gt;
  }

  // and add the putting of the new widgets into the pool.
  class PoolAddTransition extends DoTransition
  {
    private Widget w;

    public PoolAddTransition( Surface surf, Widget w )
    {
      super( surf );
      this.w = w;
    }

    public void doDo( )
    {
      m_Table.put( w.getId(), w );
      m_SortedSet.add( w );
    }
    public void doUndo()
    {
      m_Table.remove( w.getId() );
      m_SortedSet.remove( w );
    }
    public String getAction()
    {
      return "remove widget from widgetpool";
    }
  }

  public Widget get( Id id )
  {
    Widget w = (Widget)m_Table.get( id );

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

    return w;
  }

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

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

  public void print()
  {
    long lastTime = 0;
    long thisTime;
    long timeDiff;
    
    Console.writeln( "All the widgets in " + this );
    Console.indent();
    Iterator it = iterator();
    while ( it.hasNext() ) {
      Widget w = (Widget)it.next();
      thisTime = w.getTime();
      timeDiff = thisTime - lastTime;

      Console.writeln( w.toString() );
      Console.writeln( " -- Timestamp: " + w.getTime() );
      Console.writeln( "Time since last stroke: \t\t\t" + timeDiff );

      lastTime = thisTime;
    }
    
    Console.outdent();
    Console.writeln( "The distance between their times are: " ); 
  }

}

