// TicTacToeServer.java
// This class maintains a game of Tic-Tac-Toe for two
// client applets.
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.io.*;

public class TicTacToeServer extends Frame {
  private char board[];
  private boolean xMove;
  private boolean done = false;
  private TextArea output;
  private Player players[];
  private ServerSocket server;
  private int numberOfPlayers;
  private int currentPlayer;
  private int moves = 0;

  public TicTacToeServer() {
    super( "Tic-Tac-Toe Server" );
    board = new char[ 9 ];
    for (int i = 0; i<9; i++){
      board[i] = ' ';
    }
    xMove = true;
    players = new Player[ 2 ];
    currentPlayer = 0;

    // set up ServerSocket
    try {
      server = new ServerSocket( 5050, 2 );
    } catch( IOException e ) {
      e.printStackTrace();
      System.exit ( 1 );
    }

    output = new TextArea();
    add( "Center", output );
    setSize( 300, 300 );
    show();
  }

  // wait for two connections so game can be played
  public void execute() {
    for ( int i = 0; i < players.length; i++ ) {
      try {
        players[ i ] =
          new Player( server.accept(), this, i );
        players[ i ].start();
        ++numberOfPlayers;
      } catch( IOException e ) {
        e.printStackTrace();
        System.exit( 1 );
      }
    }
  }

  public int getNumberOfPlayers() { return numberOfPlayers; }

  public void display( String s ) { output.append( s + "\n" ); }

  // Determine if a move is valid.
  // This method is synchronized because only one move can be
  // made at a time.
  public synchronized boolean validMove( int loc, int player ) {
    boolean moveDone = false;

    while( player != currentPlayer ) {
      try {
        wait();
      } catch( InterruptedException e ) { }
    }

    if ( !isOccupied( loc ) ) {
      board[ loc ] = currentPlayer == 0 ? 'X' : 'O';
      currentPlayer = ++currentPlayer % 2;
      players[ currentPlayer ].otherPlayerMoved( loc );
      moves++;
      notify();    // tell waiting player to continue
      return true;
    } else return false;
  }

  public boolean isOccupied( int loc ) {
    return board[ loc ] == 'X' || board [ loc ] == 'O';
  }

  public boolean gameOverCheck() {
    boolean over = false;
    // Check rows
    for (int start=0; start < 9; start += 3)
      over = over || ((board[start] != ' ') && (board[start] == board[start+1]) && (board[start] == board[start+2]));
    // Check columns
    for (int start=0; start < 3; start++)
      over = over || ((board[start] != ' ') && (board[start] == board[start+3]) && (board[start] == board[start+6]));
    // Check diagonals
    over = over || ((board[0] != ' ') && (board[0] == board[4]) && (board[0] == board[8]));
    over = over || ((board[2] != ' ') && (board[2] == board[4]) && (board[2] == board[6]));
    if (over || (moves>=9)){
      for (int i = 0; i < players.length; i++)
        players[i].gameOver(over ? (currentPlayer + 1) % 2 : -1);
      return true;
    } else return false;
  }

  public static void main( String args[] ) {
    TicTacToeServer game = new TicTacToeServer();
    game.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    });
    game.execute();
  }
}

// Player class to manage each player as a thread
class Player extends Thread {
  Socket connection;
  DataInputStream input;
  DataOutputStream output;
  TicTacToeServer control;
  int number;
  String mark;
  boolean done = false;

  Player( Socket s, TicTacToeServer t, int num ) {
    mark = (num == 0 ? "X" : "O" );

    connection = s;

    try {
      input = new DataInputStream(
        connection.getInputStream() );
      output = new DataOutputStream(
        connection.getOutputStream() );
    } catch( IOException e ) {
      e.printStackTrace();
      System.exit( 1 );
    }

    control = t;
    number = num;
  }

  void otherPlayerMoved( int loc ) {
    try {
      output.writeUTF( "Opponent moved" );
      output.writeInt( loc );
    } catch( IOException e ) {
      e.printStackTrace();
      System.exit( 1 );
    }
  }

  void gameOver(int winner) {
    try {
      output.writeUTF( "Game Over. " +
        (( winner == -1 ) ?
          "No winner." :
          ( "Winner is " +
          ( winner == 0 ? 'X' : 'O' ) + "." ) ) );
      done = true;
      connection.close();
    } catch( IOException e ) {
      e.printStackTrace();
      System.exit( 1 );
    }
  }


  public void run() {
    try {
      control.display( "Player " +
        ( number == 0 ? 'X' : 'O' ) + " connected." );
      output.writeInt( number );
      output.writeUTF( "You are player " +
        ( number == 0 ? "X." : "O. Please wait." ) );

      // wait for another player to arrive
      if ( control.getNumberOfPlayers() < 2 ) {
        output.writeUTF( "Waiting for another player." );
        while (control.getNumberOfPlayers() < 2 )
          try { sleep(1000); } catch (InterruptedException e) { } // busy waits are bad
        output.writeUTF( "Other player connected. Your move." );
      }

      // Play game
      while ( !done ) {
        int location = input.readInt();
        if ( control.validMove( location, number ) ) {
          control.display( "loc: " + location );
          output.writeUTF( "Valid move." );
        } else output.writeUTF( "Invalid move, try again." );
        done = control.gameOverCheck();
      }
    } catch( IOException e ) {
      System.out.println("Error on connection to client");
    }
  }
}
