package sfs3.extensions.examples.tictactoe
package game

import com.smartfoxserver.entities.User
import com.smartfoxserver.entities.data.{ISFSArray, SFSArray}

import java.util
import java.util.ArrayList
import scala.collection.mutable.ArrayBuffer

class Board
{
  private var board: Array[Array[Mark]] = Array.ofDim[Mark](4, 4) 
  private var winner:Int = 0
  reset()

  def getMarkAt(row: Int, col: Int): Mark = board(row)(col)

  /**
   * Set the mark in a board slot.
   */
  def setMarkAt(row: Int, col: Int, mark: Mark): Unit = {
    // Validate coordinates
    checkCoords(row, col)
    // Set mark
    board(row)(col) = mark
  }

  /**
   * Return the last winner:
   * - 0: no winner (tie or game still in progress)
   * - 1: player 1 was the winner
   * - 2: player 2 was the winner
   */
  def getWinner: Int = winner

  /**
   * Get the current board state.
   */
  def getState(moveCount: Int): State = {
    val iter = getAllSolutions.iterator
    var state = State.RUNNING
    
    while (iter.hasNext) {
      val solution = iter.next

      if (solution == "111") {
        state = State.END_WITH_WINNER
        winner = 1
        return state
      }
      else if (solution == "222") {
        state = State.END_WITH_WINNER
        winner = 2
        return state
      }
    }
    
    if (winner == 0 && moveCount == 9) state = State.END_WITH_TIE
      state
  }

  /**
   * Reset the board adding an empty mark in all board slots.
   */
  def reset(): Unit = {
    winner = 0
    for (r <- 1 to 3) {
      val boardRow = board(r)
      for (c <- 1 to 3) {
        boardRow(c) = Mark.EMPTY
      }
    }
  }

  /**
   * Validate the passed board slot coordinates.
   */
  private def checkCoords(row: Int, col: Int): Unit = {
    if (row < 1 || row > 3) throw new IllegalArgumentException("Mark row position out of range: " + row)
    if (col < 1 || col > 3) throw new IllegalArgumentException("Mark column position out of range: " + col)
  }

  /**
   * Get all completed rows, columns and diagonals to check if the game ended.
   */
  private def getAllSolutions = {
    val solutions = new ArrayBuffer[String]()
    //--- All rows ------------------------------------------------
    for (r <- 1 to 3) {
      solutions.addOne(String.format("%s%s%s", String.valueOf(board(r)(1).id), String.valueOf(board(r)(2).id), String.valueOf(board(r)(3).id)))
    }
    //--- All columns ---------------------------------------------
    for (c <- 1 to 3) {
      solutions.addOne(String.format("%s%s%s", String.valueOf(board(1)(c).id), String.valueOf(board(2)(c).id), String.valueOf(board(3)(c).id)))
    }
    
    //--- All diagonals ------------------------------------------
    solutions.addOne(String.format("%s%s%s", String.valueOf(board(1)(1).id), String.valueOf(board(2)(2).id), String.valueOf(board(3)(3).id)))
    solutions.addOne(String.format("%s%s%s", String.valueOf(board(1)(3).id), String.valueOf(board(2)(2).id), String.valueOf(board(3)(1).id)))
    solutions
  }

  /**
   * Return the game board as a SFSArray.
   * As SFSArrays use 0-based indexes and we can't add entries starting from index 1,
   * the client receiving the data will need to transform it to rows/columns.
   */
  def toSFSArray: ISFSArray = {
    val sfsa = new SFSArray()
    for (r <- 1 to 3) {
      sfsa.addIntArray(getRowAsList(r))
    }
    sfsa
  }

  private def getRowAsList(r: Int) = {
    val row = new util.ArrayList[Integer]()

    for (c <- 1 to 3) {
      row.add( board(r)(c).id )
    }
    row
  }
}
