import java.lang.*;
import java.awt.*;
import java.awt.event.*;
import java.applet.Applet;

/**
  A java component used to display a connect-four board.
  */
class BoardDisplay extends Canvas implements Runnable, MouseListener {
  // Size of each image.
  // private static final int IMAGE_WIDTH = 50;
  // private static final int IMAGE_HEIGHT = 50;

  // RLL
  private int IMAGE_WIDTH = 50;
  private int IMAGE_HEIGHT = 50;
  private int current_image=0;
  //private int IMAGE_WIDTH = 32;
  //private int IMAGE_HEIGHT = 32;
  //private int current_image=1;

  // Images for displaying the board contents (chess needs a lot of them)
  private static Image rr_pawn_image = null;
  private static Image rb_pawn_image = null;
  private static Image br_pawn_image = null;
  private static Image bb_pawn_image = null;
  private static Image rr_rook_image = null;
  private static Image rb_rook_image = null;
  private static Image br_rook_image = null;
  private static Image bb_rook_image = null;
  private static Image rr_knight_image = null;
  private static Image rb_knight_image = null;
  private static Image br_knight_image = null;
  private static Image bb_knight_image = null;
  private static Image rr_bishop_image = null;
  private static Image rb_bishop_image = null;
  private static Image br_bishop_image = null;
  private static Image bb_bishop_image = null;
  private static Image rr_queen_image = null;
  private static Image rb_queen_image = null;
  private static Image br_queen_image = null;
  private static Image bb_queen_image = null;
  private static Image rr_king_image = null;
  private static Image rb_king_image = null;
  private static Image br_king_image = null;
  private static Image bb_king_image = null;
  private static Image r_empty_image = null;
  private static Image b_empty_image = null;

  // Board object that this is responsible for displaying
  private Board my_board;

  // Added June 16, 1999 by RL
  private Board run_board;
  private Board ponder_board;
  private Move  lastMove;

  // Applet containing this board display
  private Game my_appl;

  // Thread used to let the computer choose and make its move.
  private Thread computer_thread = null;
  private Thread ponder_thread = null;
  private Thread my_thread = null;

  // Which side gets to make the next move.
  // Zero when the game is over.
  private int turn = Board.R_SIDE;
  
  // Which side is the human?
  private int human_side = Board.R_SIDE;

  // Currently selected row and column
  private int selected_row = -1, selected_col;

  private boolean flip_board=false;

  public BoardDisplay( Board board, Game appl )
  {

    // Remember the applet that created us.
    my_appl = appl;

    // Remember the image we are supposed to display
    my_board = board;

    // RL 
    addMouseListener(this);

    // RL July 10, 1999
    setImage(1);
    rr_pawn_image = null;
    loadImage();

  }
    
  public void destroy() {
     removeMouseListener(this);
  }

  // RL July 10, 199
  private void loadImage()
  {
    Board board = my_board;
    Game appl = my_appl;
 
    // Media Tracker used to regulate image loading.
    MediaTracker tracker = new MediaTracker( appl );

    // Load images if we don't have them yet.
    if( rr_pawn_image == null ){
      int rs = Board.R_SIDE, bs = Board.B_SIDE;
      int p = Board.PAWN,   r = Board.ROOK, 
	    b = Board.BISHOP, n = Board.KNIGHT,
          q = Board.QUEEN,  k = Board.KING,
          x = Board.EMPTY;

      // Load each of the images we need
      rr_pawn_image = appl.getImage( appl.getDocumentBase(), imageString(p, rs, rs) );
      tracker.addImage( rr_pawn_image, 0 );

      rb_pawn_image = appl.getImage( appl.getDocumentBase(), imageString(p, rs, bs) );
      tracker.addImage( rb_pawn_image, 0 );

      br_pawn_image = appl.getImage( appl.getDocumentBase(), imageString(p, bs, rs) );
      tracker.addImage( br_pawn_image, 0 );

      bb_pawn_image = appl.getImage( appl.getDocumentBase(), imageString(p, bs, bs) );
      tracker.addImage( bb_pawn_image, 0 );

      rr_rook_image = appl.getImage( appl.getDocumentBase(), imageString(r, rs, rs) );
      tracker.addImage( rr_rook_image, 0 );

      rb_rook_image = appl.getImage( appl.getDocumentBase(), imageString(r, rs, bs) );
      tracker.addImage( rb_rook_image, 0 );

      br_rook_image = appl.getImage( appl.getDocumentBase(), imageString(r, bs, rs) );
      tracker.addImage( br_rook_image, 0 );

      bb_rook_image = appl.getImage( appl.getDocumentBase(), imageString(r, bs, bs) );
      tracker.addImage( bb_rook_image, 0 );

      rr_knight_image = appl.getImage( appl.getDocumentBase(), imageString(n, rs, rs) );
      tracker.addImage( rr_knight_image, 0 );

      rb_knight_image = appl.getImage( appl.getDocumentBase(), imageString(n, rs, bs) );
      tracker.addImage( rb_knight_image, 0 );

      br_knight_image = appl.getImage( appl.getDocumentBase(), imageString(n, bs, rs) );
      tracker.addImage( br_knight_image, 0 );

      bb_knight_image = appl.getImage( appl.getDocumentBase(), imageString(n, bs, bs) );
      tracker.addImage( bb_knight_image, 0 );

      rr_bishop_image = appl.getImage( appl.getDocumentBase(), imageString(b, rs, rs) );
      tracker.addImage( rr_bishop_image, 0 );

      rb_bishop_image = appl.getImage( appl.getDocumentBase(), imageString(b, rs, bs) );
      tracker.addImage( rb_bishop_image, 0 );

      br_bishop_image = appl.getImage( appl.getDocumentBase(), imageString(b, bs, rs) );
      tracker.addImage( br_bishop_image, 0 );

      bb_bishop_image = appl.getImage( appl.getDocumentBase(), imageString(b, bs, bs) );
      tracker.addImage( bb_bishop_image, 0 );

      rr_queen_image = appl.getImage( appl.getDocumentBase(), imageString(q, rs, rs) );
      tracker.addImage( rr_queen_image, 0 );

      rb_queen_image = appl.getImage( appl.getDocumentBase(), imageString(q, rs, bs) );
      tracker.addImage( rb_queen_image, 0 );

      br_queen_image = appl.getImage( appl.getDocumentBase(), imageString(q, bs, rs) );
      tracker.addImage( br_queen_image, 0 );

      bb_queen_image = appl.getImage( appl.getDocumentBase(), imageString(q, bs, bs) );
      tracker.addImage( bb_queen_image, 0 );

      rr_king_image = appl.getImage( appl.getDocumentBase(), imageString(k, rs, rs) );
      tracker.addImage( rr_king_image, 0 );

      rb_king_image = appl.getImage( appl.getDocumentBase(), imageString(k, rs, bs) );
      tracker.addImage( rb_king_image, 0 );

      br_king_image = appl.getImage( appl.getDocumentBase(), imageString(k, bs, rs) );
      tracker.addImage( br_king_image, 0 );

      bb_king_image = appl.getImage( appl.getDocumentBase(), imageString(k, bs, bs) );
      tracker.addImage( bb_king_image, 0 );

      r_empty_image = appl.getImage( appl.getDocumentBase(), imageString(x, x, rs) );
      tracker.addImage( r_empty_image, 0 );

      b_empty_image = appl.getImage( appl.getDocumentBase(), imageString(x, x, bs) );
      tracker.addImage( b_empty_image, 0 );

      // Force the tracker to load all of our images.
      do{
        try{
          tracker.waitForAll();
        }
        catch( InterruptedException e ){
        }
      }while( !tracker.checkAll() );
    }


    // Set the size of this component according to what it contains.
    setSize( IMAGE_WIDTH * Board.WIDTH, IMAGE_HEIGHT * Board.HEIGHT );

    // Resize the Applet
    my_appl.setSize( IMAGE_WIDTH * Board.WIDTH+10, IMAGE_HEIGHT * Board.HEIGHT + 70);
    my_appl.validate();
  }
  
  /**
    Restart the game.
    */
  public void restart()
  { 
    Board.startAll();

    // Kill of the computer move thread if it's trying to make a move.
    terminateComputerThread();

    // Reset the starting board configuration
    my_board.startingConfig();
    turn = Board.R_SIDE;
    human_side = Board.R_SIDE;
    selected_row = -1;
    flip_board=false;
    loadImage();

    // Redraw with the new configuration
    repaint();

    // Tell the user it's time to move.
    my_appl.setStateMessage( "Select a move." );

    lastMove = null;
    
    //startPondering();
  }
  
  /**
    Flip the board.
    */
  public void flip()
  {
    // Kill of the computer move thread if it's trying to make a move.
    terminateComputerThread();

	// Switch sides and switch who has the current turn.
    human_side = -human_side;
    turn = -turn;

    flip_board = !flip_board;

    switchTurns();
  }

  /**
    A java trick, don't bother to erase on an update.  
    It all gets redrawn in paint() anyway.
    */
  public void update( Graphics g )
  {
    paint( g );
  }
  
  /**
    Paint a representation of the current board state.
   */
  public void paint( Graphics g )
  {
    int x, y;

    if (flip_board) y = IMAGE_HEIGHT*(Board.HEIGHT-1);
    else y = 0;
    for( int row = 0; row < Board.HEIGHT; row++ ){
      if (flip_board) x = IMAGE_WIDTH*(Board.WIDTH-1);
      else x = 0;
      for( int col = 0; col < Board.WIDTH; col++ ){
        // Fill in this space with the appropariate image
        Image img;
        if( ( row + col ) % 2 == 0 )
          img = r_empty_image;
        else
          img = b_empty_image;

        // changed June 15, 1999 by RL
        // int sym = my_board.space[row][col];
        int sym = my_board.Space(row, col);

        // Decide which image we should use for this space.
        if( sym == Board.R_PAWN )
          if( ( row + col ) % 2 == 0 )
            img = rr_pawn_image;
          else
            img = rb_pawn_image;

        if( sym == Board.B_PAWN )
          if( ( row + col ) % 2 == 0 )
            img = br_pawn_image;
          else
            img = bb_pawn_image;

        if( sym == Board.R_ROOK )
          if( ( row + col ) % 2 == 0 )
            img = rr_rook_image;
          else
            img = rb_rook_image;

        if( sym == Board.B_ROOK )
          if( ( row + col ) % 2 == 0 )
            img = br_rook_image;
          else
            img = bb_rook_image;

        if( sym == Board.R_KNIGHT )
          if( ( row + col ) % 2 == 0 )
            img = rr_knight_image;
          else
            img = rb_knight_image;

        if( sym == Board.B_KNIGHT )
          if( ( row + col ) % 2 == 0 )
            img = br_knight_image;
          else
            img = bb_knight_image;

        if( sym == Board.R_BISHOP )
          if( ( row + col ) % 2 == 0 )
            img = rr_bishop_image;
          else
            img = rb_bishop_image;

        if( sym == Board.B_BISHOP )
          if( ( row + col ) % 2 == 0 )
            img = br_bishop_image;
          else
            img = bb_bishop_image;

        if( sym == Board.R_QUEEN )
          if( ( row + col ) % 2 == 0 )
            img = rr_queen_image;
          else
            img = rb_queen_image;

        if( sym == Board.B_QUEEN )
          if( ( row + col ) % 2 == 0 )
            img = br_queen_image;
          else
            img = bb_queen_image;

        if( sym == Board.R_KING )
          if( ( row + col ) % 2 == 0 )
            img = rr_king_image;
          else
            img = rb_king_image;

        if( sym == Board.B_KING )
          if( ( row + col ) % 2 == 0 )
            img = br_king_image;
          else
            img = bb_king_image;

        // Draw the chosen image.
        g.drawImage( img, x, y, my_appl );

        if( selected_row == row && selected_col == col ){
          g.setColor( Color.green );
          g.drawRect( x, y, IMAGE_WIDTH - 1, IMAGE_HEIGHT - 1 );
          g.drawRect( x + 1, y + 1, IMAGE_WIDTH - 3, IMAGE_HEIGHT - 3 );
        }

        if(lastMove != null) {
          if (lastMove.from_row() == row && lastMove.from_col() == col ) {
            g.setColor( Color.blue );
            g.drawRect( x, y, IMAGE_WIDTH - 1, IMAGE_HEIGHT - 1 );
            g.drawRect( x + 1, y + 1, IMAGE_WIDTH - 3, IMAGE_HEIGHT - 3 );
          } else if (lastMove.to_row() == row && lastMove.to_col() == col ) {
            g.setColor( Color.red );
            g.drawRect( x, y, IMAGE_WIDTH - 1, IMAGE_HEIGHT - 1 );
            g.drawRect( x + 1, y + 1, IMAGE_WIDTH - 3, IMAGE_HEIGHT - 3 );
          }
        }

        // Step ahead to the next column on the screen.
        if (flip_board) x -= IMAGE_WIDTH;
        else x += IMAGE_WIDTH;
      }

      // Step ahead to the next row on the screen
      if (flip_board) y -= IMAGE_HEIGHT;
      else y += IMAGE_HEIGHT;
    }
  }


  public void mousePressed(MouseEvent e) {
  }

  public void mouseClicked(MouseEvent e) {
  }

  public void mouseEntered(MouseEvent e) {
  }

  public void mouseExited(MouseEvent e) {
  }

  
  //
  // Respond to user mouse clicks, generally, when the user is making
  // a move.
  //
  // public boolean mouseDown(Event evt, int x, int y) {

  public void mouseReleased(MouseEvent e) {
    int x,y;
    if (flip_board){
      y = IMAGE_HEIGHT*Board.HEIGHT - e.getY();
      x = IMAGE_WIDTH*Board.WIDTH - e.getX();
    } else {
      y = e.getY();
      x = e.getX();
    }

    // Don't let the user choose anything if the game is
    // over or if it's the computer's turn to move.
    if( turn != human_side )
      return;
  
    // First, figure out row and column of the selected space
    int row = y / IMAGE_HEIGHT;
    int col = x / IMAGE_WIDTH;

    // Make sure the selected space is actually on the board
    if( row < 0 || col < 0 || row >= Board.HEIGHT ||
        col >= Board.WIDTH )
      return;

    // See if this is the choice for the source or destination position.
    if( selected_row >= 0 ){
      // See if the user is trying to un-select the chosen space.
      if( row == selected_row && col == selected_col ){
        // Flag the current selection as empty.
        selected_row = -1;
        repaint();
      } else {
        // Get the list of all legal moves from the current board.
        Move move_list = my_board.legalMoves( turn );
        Move m;
        
        // Look for the user's choice on that list.
        // changed June 16, 1999 by RL
        // change reference to from_row, from_col, to_row, to_col as methods
        // for( m = move_list; m != null && 
        //        ( m.from_row() != selected_row || m.from_col() != selected_col ||
        //          m.to_row() != row || m.to_col() != col );
        //      m = m.next );
        for( m = move_list; m != null && 
               ( m.from_row() != selected_row || m.from_col() != selected_col ||
                 m.to_row() != row || 
                ( m.to_col() != col &&
                  (m.piece() != Board.KING*human_side || 
                   (((selected_col-2 != col && col != 0) || m.from_col()-2 < m.to_col()) && 
                    ((selected_col+2 != col && col != Board.WIDTH-1) || m.from_col()+2 > m.to_col()) ) )
                ) );
             m = m.next );

        
        // If the chosen move is legal, apply it.
        if( m != null ){
          m.apply( my_board );
          lastMove = m;

          // Turn off the currently selected position.
          selected_row = -1;
          
          // Try to let the computer have a turn.
          switchTurns();
        }
        // RLL
         else if (my_board.Space(row,col)*human_side == -Board.KING &&
                  my_board.Space(selected_row,selected_col)*human_side == Board.KING){
                  toggleImage();
        }
      }
    } else {
      // Make sure the user selected a space occupied by one of their
      // pieces.
      // changed June 15, 1999 by RL
      // if( my_board.space[ row ][ col ] * human_side >= 1 ){
      if( my_board.Space(row,col) * human_side >= 1 ){
        selected_row = row;
        selected_col = col;
        repaint();
      }
    }

  }

  public void switchTurns()
  {
    int winner = my_board.checkForWin();

    // See if either side has won.
    if( winner != 0 ){
      turn = 0;

      // changed June 23,1999   by RL
      // win/lose/draw
      if( winner == -human_side )
        my_appl.setStateMessage( "Game over. You lose!" );
      else
      if( winner == human_side )
        my_appl.setStateMessage( "Game over. You Win!" );
      else
        my_appl.setStateMessage( "Game over. It's a Draw!" );
    } else {
      // No winner yet.  Can we switch Turns?
      if( my_board.legalMoves( -turn ) != null )
        // We can switch turns.
        turn = -turn;
      else
        // See if there are any legal moves left.
        if( my_board.legalMoves( turn ) == null )
          turn = 0;
      
      // Show a message for the current state.
      if( turn == 0 )
        my_appl.setStateMessage( "Game over. It's a tie." );
      
      if( turn == -human_side ){
        my_appl.setStateMessage( "Selecting computer move." );
        if (ponder_thread != null && ponder_board.Hint(lastMove))
          startThinking();
        else
          startComputerMove();
      }
      
      if( turn == human_side ) {
        my_appl.setStateMessage( "Select your move." );
        startPondering();
      }

    }

    repaint();
  }
    
  //
  // Terminate the computer thread early, before it has completed
  // making its move.
  //
  private void terminateComputerThread()
  {
    if( computer_thread != null ){
      // added June 16,1999 by RL
      if (run_board != null) run_board.stop();
      //computer_thread.stop();
      computer_thread = null;
    }
    terminatePonderThread();
  }

  //
  // Put the move made by the computer into the current board.
  // This is a separate method so that it can be synchronized.
  //
  private void submitComputerMove( Board alt_board )
  {

    // Switch over to the new alt_board.
    my_board = alt_board.Duplicate(my_board);

    lastMove = my_board.LastMove();

    // See if the game is over
    switchTurns();

    // Flag that the computer move is done.
    computer_thread = null;

    // RL 
    System.gc();
  }

  //
  // Select a new computer move, then exit.
  // Computer moves are made in a separate thread.  That way, we don't
  // tie up the user's thread for other UI activity.
  //
  // change June 16, 1999 by RL 
  // change alt_board to run_board
  public void run()
  {
    Thread tmp=my_thread;

    if (ponder_thread == my_thread) {
      ponder_board = my_board.Duplicate(null);

      ponder_board.Ponder( -human_side );
    } else
    if (computer_thread == my_thread) {
      // Make a scratch copy of the current board to work with.
      // Board alt_board = my_board.Duplicate();
      run_board = my_board.Duplicate(null);

      // Now, let the computer choose a response.
      run_board.computerMove( -human_side );
    }

    if (computer_thread == tmp) {
      // Apply the computer's move to the main board.
      submitComputerMove( run_board );
    } else
    if (ponder_thread == tmp) 
       ponder_thread = null;

  }

  /**
    Start up a spare thread and use it to let the computer choose it's move.
   */
  private void startComputerMove()
  {
    // Update the state of the applet.
    my_appl.disableDefer();

    terminatePonderThread();

    // Make a new thread in which the computer can choose
    // its move.
    computer_thread = new Thread( this );
    my_thread = computer_thread;
    computer_thread.start();
  }

  /**
    Start up a spare thread and use it to let the computer pondering on 
    opponent's time.
   */
  private void startPondering()
  {
    ponder_thread = new Thread( this );
    my_thread = ponder_thread;
    ponder_thread.start();
  }

  private void terminatePonderThread()
  {
    if( ponder_thread != null ) {
      if (ponder_board != null) ponder_board.stop();
      // ponder_thread.stop();
      ponder_thread = null;
    }
  }

  private void startThinking()
  {
      Board tmp=run_board;
      run_board = ponder_board;
      ponder_board = tmp;
      computer_thread = ponder_thread;
      ponder_thread = null;
      run_board.Switch();
  }

  public void stop()
  {
     Board.stopAll();
     terminateComputerThread();
  }

  private String imageString(int p, int col, int sq)
  {
    if (current_image==0) {
      String[] ps = { "empty", "pawn", "rook", "knight", "bishop", "queen", "king"};
      String[] ss = { "r", "",  "b" };
 
      // "rb.pawn.gif" "r.empy.gif"
      return ss[col+1] + ss[sq+1] + "." + ps[p] + ".gif";  
    } else {
      String[] ps = { "0", "p", "r", "n", "b", "q", "k"};
      String[] ss = { "w", "",  "b" };
 
      // "bp-w.gif" "0-w.gif"
      return ss[col+1] + ps[p] + "-" + ss[sq+1] + ".gif";  
    }
  }

  // RLL
  private void toggleImage()
  {

    if (current_image == 0)
      setImage(1);
    else
      setImage(0);

    // RL July 10, 199
    rr_pawn_image = null;
    loadImage();
    System.gc();

  }


  private void setImage(int img)
  {
     current_image = img;
     switch(img)
     {
       case 0:
          IMAGE_WIDTH = 50;
          IMAGE_HEIGHT = 50;
          break;
       case 1:
          IMAGE_WIDTH = 32;
          IMAGE_HEIGHT = 32;
          break;
     }
  }

}

