/** A tic-tac-toe game. */
public class TicTacToe {
  
  /** The tic-tac-toe board. */
  private char[][] board;
  
  /** The board dimension. */
  // This is a constant.  Once it has been initialized
  // it cannot be reassigned.
  public static final int BOARD_SIZE = 3;
  
  /** The blank square. */
  public static final char BLANK_SQUARE = ' ';
    
  /** Create a new tic-tac-toe game. */
  public TicTacToe() {
    this.board = new char[BOARD_SIZE][BOARD_SIZE];
    
    // for each row
    for (int r = 0; r < BOARD_SIZE; r++) {
      
      // for each column
      for (int c = 0; c < BOARD_SIZE; c++) {
        board[r][c] = BLANK_SQUARE;
      }
    }
  }
  
  /**
   * Put mark m at row r and column c.
   * @param m The mark to put on the board.
   * @param r The index of the row.
   * @param c The index of the column.
   */ 
  public void setMark(char m, int r, int c) throws TTTException {
    if ((r > BOARD_SIZE - 1) 
          && (c > BOARD_SIZE - 1)) {
      throw new TTTException("Both row and column are invalid!");
    } else if (r > BOARD_SIZE - 1) {
      throw new TTTException("The row is invalid!");
    } else if (c > BOARD_SIZE - 1) {
      throw new TTTException("The column is invalid!");
    }
    
    if (board[r][c] == BLANK_SQUARE) {
      board[r][c] = m;
    }
  }
  
  /**
   * Get the String representation of this tic-tac-toe
   * game.
   */
  public String toString() {
    String temp = "";
    
    for (int r = 0; r < BOARD_SIZE; r++) {
      for (int c = 0; c < BOARD_SIZE; c++) {
        temp += this.board[r][c] + "|";
      }
      
      temp = temp.substring(0, temp.length() - 1);
      temp += '\n';
      
      for (int i = 0; i < BOARD_SIZE && r != BOARD_SIZE - 1; i++) {
        temp += "- ";
      }
      
      temp = temp.substring(0, temp.length() - 1);
      temp += '\n';
    }
    
    return temp;
  } 
  
  /**
   * Get the character who has won the game across a row,
   * and if nobody has won, then return the blank square. */
  public char rowWinner() {
    char winner = ' ';
    boolean didWin = false;
    
    for (int r = 0; r < BOARD_SIZE && !didWin; r++) {
      didWin = true;
      for (int c = 0; c < BOARD_SIZE - 1 && didWin; c++) {
        if (this.board[r][c] == BLANK_SQUARE 
              || this.board[r][c] != this.board[r][c + 1]) {
          didWin = false;
        }
      }
      
      if (didWin) {
        winner = this.board[r][BOARD_SIZE - 1];
      }
    }
    
    return winner;
  }
  
  /**
   * Get the character who has won the game down a column,
   * and if nobody has won, then return the blank square. */
  public char colWinner() {
    char winner = ' ';
    boolean didWin = false;
    
    for (int c = 0; c < BOARD_SIZE && !didWin; c++) {
      didWin = true;
      for (int r = 0; r < BOARD_SIZE - 1 && didWin; r++) {
        if (this.board[r][c] == BLANK_SQUARE 
              || this.board[r][c] != this.board[r + 1][c]) {
          didWin = false;
        }
      }
      
      if (didWin) {
        winner = this.board[BOARD_SIZE - 1][c];
      }
    }
    
    return winner;
  }
  
  /**
   * Get the character who has won the game along a diagonal,
   * and if nobody has won, then return the blank square. */
  public char diagWinner() {
    char winner = ' ';
    boolean didWin = false;
    
    // from upper-left to lower-right
    for (int r = 0; r < BOARD_SIZE - 1; r++) {
      if (this.board[r][r] == BLANK_SQUARE 
            || this.board[r][r] != this.board[r + 1][r + 1]) {
        didWin = false;
      }
    }
    
    if (didWin) {
      winner = this.board[0][0];
      return winner;
    }
    
    // from upper-right to lower-left
    for (int r = 0; r < BOARD_SIZE - 1; r++) {
      if (this.board[r][BOARD_SIZE - 1 - r] == BLANK_SQUARE) {
        didWin = false;
      } else if (this.board[r][BOARD_SIZE - 1 - r] 
                   != this.board[r + 1][BOARD_SIZE - 2 - r]) {
        didWin = false;
      }
    }
    
    if (didWin) {
      winner = this.board[0][BOARD_SIZE - 1];
    }
    
    return winner;
  }
  
  /**
   * Return the winning character. If nobody has won, then
   * return the blank square.
   */
  public char getWinner() {
    if (this.rowWinner() != BLANK_SQUARE) {
      return this.rowWinner();
    } else if (this.colWinner() != BLANK_SQUARE) {
      return this.colWinner();
    } else {
      return this.diagWinner();
    }
  }
  
  /**
   * Return whether the game has ended in a draw.
   * The game is considered to be a draw if all spaces
   * are occupied and there is no winner.
   */
  public boolean isDraw() {
    boolean draw = true;
    
    // if no winner
    if (this.getWinner() == BLANK_SQUARE) {
      for (int r = 0; r < BOARD_SIZE; r++) {
        for (int c = 0; c < BOARD_SIZE; c++) {
          if (this.board[r][c] == BLANK_SQUARE) {
            draw = false;
          }
        }
      }
           
    } else {
      draw = false;
    }
    return draw;
  }
  
  /**
   * Get the character at position (r, c).
   */
  public char getMark(int r, int c) {
    return this.board[r][c];
  }
  
}
