// Fig. 16.8: TicTacToeClient.java
// Client for the TicTacToe program
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.io.*;

// Client class to let a user play Tic-Tac-Toe with
// another user across a network.
public class TicTacToeClient extends Applet
             implements Runnable {
   private TextField id;
   private TextArea display;
   private Panel boardPanel, panel2;
   private Square board[][], currentSquare;
   private Socket connection;
   private DataInputStream input;
   private DataOutputStream output;
   private Thread outputThread;
   private char myMark;

   // Set up user-interface and board
   public void init()
   {
      setLayout( new BorderLayout() );
      display = new TextArea( 4, 30 );
      display.setEditable( false );
      add( display, BorderLayout.SOUTH );

      boardPanel = new Panel();
      boardPanel.setLayout( new GridLayout( 3, 3, 0, 0 ) );
      board = new Square[ 3 ][ 3 ];

      // When creating a Square, the location argument to the
      // constructor is a value from 0 to 8 indicating the
      // position of the Square on the board. Values 0, 1,
      // and 2 are the first row, values 3, 4, and 5 are the
      // second row. Values 6, 7, and 8 are the third row.
      for ( int row = 0; row < board.length; row++ )
         for ( int col = 0; col < board[ row ].length; col++ ) {
            board[ row ][ col ] =
               new Square( ' ', row * 3 + col );
            board[ row ][ col ].addMouseListener(
               new SquareListener(
                  this, board[ row ][ col ] ) );
            boardPanel.add( board[ row ][ col ] );
         }

      id = new TextField();
      id.setEditable( false );
      add( id, BorderLayout.NORTH );

      panel2 = new Panel();
      panel2.add( boardPanel );
      add( panel2, BorderLayout.CENTER );
   }

   // Make connection to server and get associated streams.
   // Start separate thread to allow this applet to
   // continually update its output in text area display.
   public void start()
   {
      try {
         connection =
            new Socket( InetAddress.getLocalHost(), 5000 );
         input = new DataInputStream(
            connection.getInputStream() );
         output = new DataOutputStream(
            connection.getOutputStream() );
      }
      catch ( IOException e ) {
         e.printStackTrace();         
      }

      outputThread = new Thread( this );
      outputThread.start();
   }

   // Control thread that allows continuous update of the
   // text area display.
   public void run()
   {
      // First get player's mark (X or O)
      try {
         myMark = input.readChar();
         id.setText( "You are player \"" + myMark + "\"" );
      }
      catch ( IOException e ) {
         e.printStackTrace();         
      }

      // Receive messages sent to client
      while ( true ) {
         try {
            String s = input.readUTF();
            processMessage( s );
         }
         catch ( IOException e ) {
            e.printStackTrace();         
         }
      }
   }

   // Process messages sent to client
   public void processMessage( String s )
   {
      if ( s.equals( "Valid move." ) ) {
         display.append( "Valid move, please wait.\n" );
         currentSquare.setMark( myMark );
         currentSquare.repaint();
      }
      else if ( s.equals( "Invalid move, try again" ) ) {
         display.append( s + "\n" );
      }
      else if ( s.equals( "Opponent moved" ) ) {
         try {
            int loc = input.readInt();
   
            done:
            for ( int row = 0; row < board.length; row++ )
               for ( int col = 0;
                     col < board[ row ].length; col++ )
                  if ( row * 3 + col == loc ) {
                     board[ row ][ col ].setMark(
                        ( myMark == 'X' ? 'O' : 'X' ) );
                     board[ row ][ col ].repaint();
                     break done;
                  }

            display.append(
               "Opponent moved. Your turn.\n" );
         }
         catch ( IOException e ) {
            e.printStackTrace();         
         }
      }
      else {
         display.append( s + "\n" );
      }
   }

   public void sendClickedSquare( int loc )
   {
      try {
         output.writeInt( loc );
      }
      catch ( IOException ie ) {
         ie.printStackTrace();         
      }
   }

   public void setCurrentSquare( Square s )
   {
      currentSquare = s;
   }
}

// Maintains one square on the board
class Square extends Canvas {
   private char mark;
   private int location;

   public Square( char m, int loc)
   {
      mark = m;
      location = loc;
      setSize ( 30, 30 );
   }

   public void setMark( char c ) { mark = c; }

   public int getSquareLocation() { return location; }

   public void paint( Graphics g )
   {
      g.drawRect( 0, 0, 29, 29 );
      g.drawString( String.valueOf( mark ), 11, 20 );
   }
}

class SquareListener extends MouseAdapter {
   private TicTacToeClient applet;
   private Square square;

   public SquareListener( TicTacToeClient t, Square s )
   {
      applet = t;
      square = s;
   }

   public void mouseReleased( MouseEvent e )
   {
      applet.setCurrentSquare( square );
      applet.sendClickedSquare( square.getSquareLocation() );
   }
}