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

// $Id: WSelection.java,v 1.7 2003/03/06 00:40:47 moltmans Exp $
// Time-stamp: <2000-03-22 13:56:15 moltmans>
package rec.core.widgets;


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

/** some notes on selections:
 *  selections are now made only with things which are on the surface.
 *  when a selection is made, figure out which widgets would be moved
 *  if it is moved, and which would be killed.  Make lists to keep with
 *  the selection, for fast reference.
 */
public class WSelection extends LocatableWidget
{
  private WGroup m_Selected;
  private Hashtable m_ColorTable;
  private HashSet m_Movable;
  private HashSet m_Broken;
  private HashSet m_BrokenLitter;
  private boolean m_FromLoop;
  
  public WSelection( WGroup vw, boolean fromLoop )
  {
    super( vw.getLocation().getCenter() );


    setEnglishName( "Selection" );
    m_FromLoop = fromLoop;
    m_Selected = vw;
    m_BrokenLitter = new HashSet();
    
    setupSelection();
    
    m_ColorTable = new Hashtable();
  }

  public boolean fromLoop()
  {
    return m_FromLoop;
  }
  
  public Collection getMovables()
  {
    return m_Movable;
  }

  public Collection getBroken()
  {
    return m_Broken;
  }

  public Collection getLittersToBreak()
  {
    return m_BrokenLitter;
  }
  
  public double size()
  {
    return m_Selected.size();
  }

  public void resize( double scaleFactor )
  {
    Assert.error( "resizing a selection has no meaning" );
  }


  public void move( GPoint gp )
  {
    Assert.error( "cannot move a selection -- but this assert may not apply" );
  }

  public void rotate( GPoint gp, double angle )
  {
    Assert.error( "cannot rotate a selection -- but this assert may not apply" );
  }


  public Location getLocation()
  {
    return( m_Selected.getLocation() );
  }

  public WGroup getSelected()
  {
    return m_Selected;
  }

  public Rectangle getBounds()
  {
    return( m_Selected.getBounds() );
  }

  public boolean includes( GPoint gp, double offset )
  {
    return m_Selected.includes( gp, offset );
  }

  public boolean contains( GPoint gp )
  {
    return m_Selected.contains( gp );
  }
  

  public VisibleWidget getWidget()
  {
    return( m_Selected );
  }

  public boolean isInSelection( Widget w )
  {
    return m_Selected.getWidgets().contains( w );
  }

  public Object clone()
  {
    return( new WSelection( m_Selected, m_FromLoop ) );
  }

  public void draw( Graphics g )
  {
    Rectangle r = m_Selected.getBounds();

    //super.draw( g );

    g.drawRect( r.x, r.y, r.width, r.height );
  }

  private void modifyColors()
  {
    // take the widget selected and save its color and the color
    // of all of its ancestors and children.
    Iterator it = m_Selected.getWidgets().iterator();
    while ( it.hasNext() ) {
      VisibleWidget viswid = (VisibleWidget)it.next();
      Console.writeln( "Changing the color of " + viswid );
      if ( viswid.getColor() != null ) {
	m_ColorTable.put( viswid, viswid.getColor() );
      }
      viswid.setColor( Colors.getColor( "selected" ) );
    }
  }
  

  private void restoreColors()
  {
    Iterator it = m_Selected.getWidgets().iterator();
    while ( it.hasNext() ) {
      VisibleWidget vw = (VisibleWidget)it.next();
      if ( m_ColorTable.containsKey( vw ) ) {
	Color c = (Color)m_ColorTable.get( vw );
	vw.setColor( c );
      }
      else {
	vw.setColor( null );
      }
    }
  }


  private void setupSelection()
  {
    // find the different types of affected widgets:
    //    -- look at all widgets connected to this one
    //    -- if the widget is in a litter, when the widget is moved, set
    //       the litter to null and kill the parent
    //    -- else, go up the tree, marking all ancestors as movable
    //    -- then trace over all the nodes which are connected to
    //       the selected one and if all of the widgets parents are
    //       movable, it is movable too, else it should be killed.

    Iterator selected_it;
    selected_it = m_Selected.getWidgets().iterator();

    m_Movable = new HashSet();
    m_Broken = new HashSet();

    m_Movable.add( m_Selected );

    HashSet roots = new HashSet();
    
    while ( selected_it.hasNext() ) {
      VisibleWidget base = (VisibleWidget)selected_it.next();

      Console.writeln( "One of the selected widgets is: " + base );
      
      // add the selected widget, because obviously that's movable
      m_Movable.add( base );

      // if we have a litter, then there is only one parent, it is a root,
      // and it should be killed.  Remember to break the litter later!!!
      if ( base.inLitter() ) {
	roots.addAll( base.getParents() );
	m_Broken.addAll( base.getParents() );
	m_BrokenLitter.add( base );
      }
      else {
	m_Movable.addAll( base.getAncestors() );
	roots.addAll( base.getRootAncestors() );
      }

    }

    Iterator roots_it = roots.iterator();
    while ( roots_it.hasNext() ) {
      Widget rt = (Widget)roots_it.next();

      // traverse down the children, BREDTH FIRST
      Vector control = new Vector( rt.getChildren() );
      while ( !control.isEmpty() ) {
	FamilyMember child = (FamilyMember)control.remove( 0 );
	if ( child instanceof Widget ) {
	  Collection parents = ((Widget)child).getParents();
	  if ( m_Movable.containsAll( parents ) ) {
	    m_Movable.add( child );
	  }
	  else {
	    // if it's in a litter it's not broken... a little complicated,
	    // think about it.
	    if ( !((Widget)child).inLitter() ) {
	      m_Broken.add( child );
	    }
	    else {
	      if ( !m_Movable.contains( child ) ) {
		// ignore this child
		continue;
	      }
	    }
	  }
	}
	control.addAll( child.getChildren() );
      }
      
    }

  }
  
  public void makeSelection( Surface s )
  {
    HashSet temp = new HashSet( m_Selected.getWidgets() );
    s.replaceWidget( temp, m_Selected, false );
    if ( m_Selected.size() > 1 ) {
      s.addWidgetToSurface( m_Selected );
    }
    modifyColors();
    s.enterEditMode( "Selection", this );    
  }

  /** unhook for the try again function */
  public WGroup unhookSelection( Surface s )
  {
    restoreColors();
    DoTransition dt2 =
      new SeverTransition( s, Collections.singleton( m_Selected ) );
    
    dt2.doDo();
    s.addTransition( dt2 );

    if ( m_Selected.size() > 1 ) {
      s.removeWidgetFromSurface( m_Selected );
    }

    return m_Selected;

  }

  /** rehook in the try again function */
  public void rehookSelection( WGroup g, Surface s )
  {
    if ( g.size() > 0 ) {
      m_Selected = g;
    }
    HashSet temp = new HashSet( m_Selected.getWidgets() );
    s.replaceWidget( temp, m_Selected, false );
    modifyColors();
  }

  public void removeSelection( Surface s )
  {
    s.leaveEditMode( "Selection" );
    restoreColors();
    DoTransition dt2 =
      new SeverTransition( s, Collections.singleton( m_Selected ) );
    dt2.doDo();
    s.addTransition( dt2 );
      
    DoTransition dt = new DieTransition( s, m_Selected );
    dt.doDo();
    s.addTransition( dt );

    if ( m_Selected.size() > 1 ) {
      s.removeWidgetFromSurface( m_Selected );
    }
  }
  
  // here's the transition suitcase for when the selection is elected.
  public void makeDefaultSuitcase( Surface s )
  {
    addToSuitcase( new CertainTransition( s, this ) );
    
    DoTransition dt = new DoTransition( s ) {

      public void doDo()
      {
	makeSelection( m_Surface );
      }
      
      // now set all the colors back to the way that they were
      public void doUndo()
      {
	removeSelection( m_Surface );
      }

      public String getAction()
      {
	return ( "select widget " + m_Selected );
      } 
    };
    
    addToSuitcase( dt );

  }
  
 
}

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

