package tictactoe;

public class Game
    implements GameInterface {

  private static final int XCHAR = 'X';
  private static final int OCHAR = 'O';
  private static final int EMPTYCHAR = ' ';
  private static final int DRAWCHAR = 'D';
  private static final int BADCHAR = '!';

  private int boardSize;
  private char[][] board;
  private UserInterface userX = null;
  private UserInterface userO = null;
  private UserInterface playerToMove = null;
  private char charToMove = XCHAR;
  private char winner = EMPTYCHAR;

  public Game(int boardSize) {
    this.boardSize = boardSize;
    board = new char[boardSize][boardSize];
    for (int i = 0; i < boardSize; i++)
      for (int j = 0; j < boardSize; j++)
        board[i][j] = ' ';
  }

// Tell userX to make a move
  public void startGame() {
    playerToMove = userX;
    userX.boardChanged(getBoardCopy());
    userO.boardChanged(getBoardCopy());
    userX.makeAMove();
  }

// return true on success, false on failure
  public void move(int row, int col, UserInterface user) {
    char[][] boardCopy;
    if (user != playerToMove) {
      user.inform("Sorry, there are already 2 players, try again later");
      return;
    }
    if (row < 0 || row >= boardSize || col < 0 || col >= boardSize ||
        board[row][col] != EMPTYCHAR) {
      playerToMove.inform("Please try again to make a valid move:");
      playerToMove.makeAMove();
      return;
    }
    board[row][col] = charToMove;
    if (user == userX) {
      playerToMove = userO;
      charToMove = OCHAR;
    }
    else {
      playerToMove = userX;
      charToMove = XCHAR;
    }
    setWinner();
    boardCopy = getBoardCopy();
    userX.boardChanged(boardCopy);
    userO.boardChanged(boardCopy);
    if (winner == EMPTYCHAR) {
        playerToMove.inform(charToMove + ": Please make a move:");
      playerToMove.makeAMove();
    }
    else if (winner == XCHAR) {
      userX.inform("X won!  Congratulations!");
      userO.inform("X won, better luck next time!");
    }
    else if (winner == OCHAR) {
        userO.inform("O won!  Congratulations");
        userX.inform("O Won, better luck next time!");

    }
    else {
      userX.inform("The game is a draw!");
      userO.inform("The game is a draw!");
    }
    return;
  }
  
//inform the game of this user and find out if this s X or O
  public void joinGame(UserInterface user) {
    if (userX == null) {
      userX = user;
      user.setId((char)XCHAR);
    }
    else if (userO == null) {
      userO = user;
      user.setId((char)OCHAR);
      startGame();
    }
  }
  
  private char[][] getBoardCopy() {
      char[][] copy = new char[boardSize][boardSize];
      for (int i=0;i<boardSize;i++)
          for (int j=0;j<boardSize;j++)
              copy[i][j] = board[i][j];
      return copy;
  }

  private void setWinner() {
    if (checkWinner(XCHAR))
      winner = XCHAR;
    else if (checkWinner(OCHAR))
      winner = OCHAR;
    else if (countMoves() == boardSize * boardSize)
      winner = DRAWCHAR;
    else
      winner = EMPTYCHAR;
  }

  private int countMoves() {
    int count = 0;
    for (int i = 0; i < boardSize; i++)
      for (int j = 0; j < boardSize; j++)
        if (board[i][j] != EMPTYCHAR)
          count++;
    return count;
  }

  private boolean checkWinner(int c) {
    if (checkMainDiag(c))
      return true;
    if (checkOtherDiag(c))
      return true;
    for (int i = 0; i < boardSize; i++) {
      if (checkWinnerRow(i, c))
        return true;
      if (checkWinnerCol(i, c))
        return true;
    }
    return false;
  }

  private boolean checkMainDiag(int c) {
    for (int i = 0; i < boardSize; i++)
      if (board[i][i] != c)
        return false;
    return true;
  }

  private boolean checkOtherDiag(int c) {
    for (int i = 0; i < boardSize; i++)
      if (board[i][boardSize - i - 1] != c)
        return false;
    return true;
  }

  private boolean checkWinnerRow(int row, int c) {
    for (int i = 0; i < boardSize; i++)
      if (board[row][i] != c)
        return false;
    return true;
  }

  private boolean checkWinnerCol(int col, int c) {
    for (int i = 0; i < boardSize; i++)
      if (board[i][col] != c)
        return false;
    return true;
  }

// return a string that can display the board nicely
// this is a convenience for a text-based interface and for debugging
  public String toString() {
    String s;
    s = "boardsize is " + boardSize + " by " + boardSize + "\n";
    if ( (winner == 'X') || (winner == 'O'))
      s = s + winner + " won!\n";
    else if (winner == 'D')
      s = s + "game is a draw\n";
    else if (playerToMove == null)
      s = s + "game not started yet\n";
    else
      s = s + "next player is " + charToMove + "\n";

    for (int i = 0; i < boardSize; i++) {
      for (int j = 0; j < boardSize; j++) {
        s = s + " " + board[i][j] + " ";
        if (j < boardSize - 1)
          s = s + "|";
      }
      s = s + "\n";
      if (i < boardSize - 1) {
        for (int j = 0; j < boardSize; j++) {
          s = s + "---";
          if (j < boardSize - 1)
            s = s + "|";
        }
        s = s + "\n";
      }
    }
    return s;
  }

}


