/**
 * FILE: Stable.java
 * AUTHOR: Ivan J Leichtling
 * INERNET: ivan@columbia.edu
 * 
 * This file defines the main stable marraige applet.
 */
import java.util.*;
import java.applet.*;
import java.awt.*;
import java.net.*;

public class Stable extends Applet
{
  
  int iCouples;
  Vector vMen;
  Vector vWomen;
  boolean fNotAllEngaged;
  Enumeration e;
  int state;
  Button bStep, bRun, bReset;
  TextArea taSpew;
 
  public void init()
  {
    String s_Clip;
    if( null != (s_Clip = getParameter( "SOUND" ) ) ) {
      AudioClip ac = getAudioClip( getDocumentBase(), s_Clip );
      ac.loop();
    }

    iCouples = getParam( "COUPLES", getParam( "MESSAGEWIDTH", 60 ) );
    
    vWomen = new Vector();
    vMen = new Vector();

    taSpew = new TextArea( 6, 60 );
    taSpew.setEditable( false );

    for(int i = 1; i <= iCouples; i++ ) {
      vWomen.addElement( 
	new Woman( getParam( "Woman" + i, "Woman " + i ), taSpew ) );
    }

    for(int i = 1; i <= iCouples; i++ ) {
      vMen.addElement( new Man( getParam( "Man" + i, "Man " + i ), taSpew ) );
    }

    reset();
    initUI();

    repaint();
  }

  public void initUI()
  {
    setBackground( Color.pink );
    setForeground( Color.black );

    GridBagLayout gbl = new GridBagLayout();
    GridBagConstraints gbc = new GridBagConstraints();
    
    setLayout( gbl );
    
    /*    // The label at the top 
    Label l_Title = new Label( "Stable Marraige", Label.CENTER );
    l_Title.setFont( new Font( "Helvetica", Font.BOLD, 18 ) );
    gbc.gridwidth = GridBagConstraints.REMAINDER;
    gbc.gridheight = 2;
    gbl.setConstraints( l_Title, gbc );
    add( l_Title );
    */

    // Labels for Columns of Buttons
    Label l_Women = new Label( "Women", Label.CENTER );
    Label l_Men = new Label( "Men", Label.CENTER );
    Insets insetW = new Insets( 1, 30, 1, 60 );
    Insets insetM = new Insets( 1, 60, 1, 30 );
    gbc.gridwidth = GridBagConstraints.RELATIVE;
    gbc.gridheight = 1;
    gbc.insets = insetW;
    gbc.anchor = GridBagConstraints.WEST;
    gbl.setConstraints( l_Women, gbc );
    add( l_Women );
    gbc.gridwidth = GridBagConstraints.REMAINDER;
    gbc.anchor = GridBagConstraints.EAST;
    gbc.insets = insetM;
    gbl.setConstraints( l_Men, gbc );
    add( l_Men );

    // Columns of Buttons
    Enumeration enWomen = vWomen.elements();
    Enumeration enMen = vMen.elements();

    gbc.fill = GridBagConstraints.NONE;
    
    for( int i = 0; i < iCouples; i++ ) {
      gbc.gridwidth = GridBagConstraints.RELATIVE;
      gbc.insets = insetW;
      gbc.anchor = GridBagConstraints.WEST;
      PersonButton pbW = new PersonButton( (Woman)enWomen.nextElement() );
      gbl.setConstraints( pbW, gbc );
      add( pbW );
      
      gbc.gridwidth = GridBagConstraints.REMAINDER;
      gbc.insets = insetM;
      gbc.anchor = GridBagConstraints.EAST;
      PersonButton pbM = new PersonButton( (Man)enMen.nextElement() );
      gbl.setConstraints( pbM, gbc );
      add( pbM );
    }

    Panel pButtons = new Panel();
    GridBagLayout gblB = new GridBagLayout();
    GridBagConstraints gbcB = new GridBagConstraints();
    pButtons.setLayout( gblB );
    
    bStep = new Button( "Step" );
    bRun = new Button( "Run" );
    bReset = new Button( "Reset" );
    
    gbcB.gridwidth = 1;
    gbcB.insets = new Insets( 1, 3, 1, 3 );
    gbcB.anchor = GridBagConstraints.CENTER;
    gblB.setConstraints( bStep, gbcB );
    pButtons.add( bStep );
    
    gbcB.gridwidth = 1;
    gblB.setConstraints( bRun, gbcB );
    pButtons.add( bRun );
    
    gbcB.gridwidth = GridBagConstraints.REMAINDER;
    gblB.setConstraints( bReset, gbcB );
    pButtons.add( bReset );

    gbc.anchor = GridBagConstraints.CENTER;
    gbc.insets = new Insets( 2, 6, 0, 6 );
    gbl.setConstraints( pButtons, gbc );
    add( pButtons );
    pButtons.show();

    gbc.fill = GridBagConstraints.HORIZONTAL;
    gbl.setConstraints( taSpew, gbc );
    add( taSpew );
  }
  
  public void reset()
  {
    Random rand = new Random();
    rand.setSeed( new Date().getTime() );

    Enumeration en = vMen.elements();
    while( en.hasMoreElements() ) {
      Person p = (Person) en.nextElement();
      p.reset();
      Vector v = (Vector) vWomen.clone();
      p.randomPrefs( v, rand );
    }

    en = vWomen.elements();
    while( en.hasMoreElements() ) {
      Person p = (Person) en.nextElement();
      p.reset();
      Vector v = (Vector) vMen.clone();
      p.randomPrefs( v, rand );
    }
    taSpew.setText( "" );

    fNotAllEngaged = true;
    e = null;
    state = 1;
    repaint();

    /* This duplicate code has been added as a bug fix. */
    en = vMen.elements();
    while( en.hasMoreElements() ) {
      Person p = (Person) en.nextElement();
      p.reset();
      Vector v = (Vector) vWomen.clone();
      p.randomPrefs( v, rand );
    }

    en = vWomen.elements();
    while( en.hasMoreElements() ) {
      Person p = (Person) en.nextElement();
      p.reset();
      Vector v = (Vector) vMen.clone();
      p.randomPrefs( v, rand );
    }
    repaint();

  }
  
  public void printAllPrefs()
  {
    Enumeration en = vMen.elements();
    while( en.hasMoreElements() ) {
      Person p = (Person) en.nextElement();
      p.printPrefs();
    }

    en = vWomen.elements();
    while( en.hasMoreElements() ) {
      Person p = (Person) en.nextElement();
      p.printPrefs();
    }
  }
  
  public void step()
  {
    switch( state )
      {
      case 1:
	if( fNotAllEngaged ) {
	  fNotAllEngaged = false;
	  e = vMen.elements();
	  state = 2;
	}
	else {
	  state = 4;
	  printCouples();
	  bRun.disable();
	  bStep.disable();
	}
	break;

      case 2:
	if( e.hasMoreElements() ) {
	  Man m = (Man) e.nextElement();
	  if( !m.getEngaged() ) {
	    m.propose();
	    fNotAllEngaged = true;
	  }
	  else {
	    step();
	  }
	}
	else {
	  e = vWomen.elements();
	  state = 3;
	}
	break;

      case 3:
	if( e.hasMoreElements() ) {
	  Woman w = (Woman) e.nextElement();
	  if( w.hasSuitors() ) {
	    w.decide();
	  }
	  else {
	    step();
	  }
	}
	else {
	  state = 1;
	}
	break;

      case 4:
	break;
      }

    repaint();
  }
  
  public void findMatches()
  {
    while( state != 4 ) {
      step();
    }
  }

  public void printCouples()
  {
    Enumeration en = vMen.elements();
    while( en.hasMoreElements() ) {
      Man m = (Man) en.nextElement();
      m.print( m.getName() + " is engaged to " + 
	       m.getFiancee().getName() );
    }
  }

  public void paint( Graphics g )
  {
    Enumeration enum = vMen.elements();

    while( enum.hasMoreElements() ) {

      Man m = (Man)enum.nextElement();

      if( m.getEngaged() ) {
	g.setColor( Color.red );

	Point pM = m.getButton().location();
	Dimension dM = m.getButton().size();

	Point pF = m.getFiancee().getButton().location();
	Dimension dF = m.getFiancee().getButton().size();

	g.drawLine( pM.x,
		    pM.y + (dM.height / 2 ),
		    pF.x + dF.width,
		    pF.y + (dF.height / 2 ) );
      }
      else if( m.getProposed() ) {
	g.setColor( Color.blue );

	Point pM = m.getButton().location();
	Dimension dM = m.getButton().size();

	Point pF = m.getPossible().getButton().location();
	Dimension dF = m.getPossible().getButton().size();

	g.drawLine( pM.x,
		    pM.y + (dM.height / 2 ),
		    pF.x + dF.width,
		    pF.y + (dF.height / 2 ) );
      }
    }
  }

  public boolean handleEvent( Event evt )
  {
    switch( evt.id )
      {
      case Event.ACTION_EVENT:
	if( evt.target == bReset ) {
	  bRun.disable();
	  bStep.disable();
	  reset();
	  bRun.enable();
	  bStep.enable();
	  return( true );
	}
	else if( evt.target == bRun ) {
	  bRun.disable();
	  bStep.disable();
	  while( 4 != state ) {
	    step();
	  }
	  return( true );
	}
	else if( evt.target == bStep ) {
	  //	  bRun.disable();
	  //	  bStep.disable();
	  step();
	  //	  bRun.enable();
	  //	  bStep.enable();
	  return( true );
	}
      default:
	return( super.handleEvent( evt ) );
      }
  }

  private String getParam( String s, String use )
  {
    if( null == (s = getParameter( s ) ) ) {
      return( use );
    }
    return( s );
  }

  private int getParam( String s, int i )
  {
    try
      {
	return Integer.parseInt( getParameter( s ) );
      }
    catch( NumberFormatException ex )
      {
	return i;
      }
  }

  public String getAppletInfo()
  {
    return( "This applet designed and destroyed by:\n"
	    + "Ivan J Leichtling\n" + "ivan@columbia.edu\n" );
  }

  public String[][] getParameterInfo()
  {
    String[][] s = new String[5][3] ;
    s[0][0] = "SOUND";
    s[0][1] = "File";
    s[0][2] = "Au file to play in the background";
    s[1][0] = "COUPLES";
    s[1][1] = "Integer";
    s[1][2] = "A number of couples to create";
    s[2][0] = "MAN#";
    s[2][1] = "String";
    s[2][2] = "the name of man #";
    s[3][0] = "WOMAN#";
    s[3][1] = "String";
    s[3][2] = "the name of woman #";
    s[4][0] = "MESSAGEWIDTH";
    s[4][1] = "Integer";
    s[4][2] = "the width (in characters) of the text box which displays output";

    return( s );
  }
}




      



