package de.nowchess.bot

import de.nowchess.api.board.{Color, File, PieceType, Square}
import de.nowchess.api.game.GameContext
import scala.util.Random

object ZobristHash:

  // 768 entries: 64 squares * 12 piece variants (2 colors * 6 piece types)
  private val pieceRands: Array[Long] = Array.ofDim(768)

  // Side-to-move: XOR when Black to move
  private val sideToMoveRand: Long = Random(0x1BADB002L).nextLong()

  // 4 entries: White kingside, White queenside, Black kingside, Black queenside
  private val castlingRands: Array[Long] = Array.ofDim(4)

  // 8 entries: one per file (a-h)
  private val enPassantRands: Array[Long] = Array.ofDim(8)

  // Initialize all random values using a seeded RNG for reproducibility
  locally:
    val rng = Random(0x1BADB002L)
    for i <- 0 until 768 do
      pieceRands(i) = rng.nextLong()
    for i <- 0 until 4 do
      castlingRands(i) = rng.nextLong()
    for i <- 0 until 8 do
      enPassantRands(i) = rng.nextLong()

  /** Compute a 64-bit Zobrist hash for a GameContext. */
  def hash(context: GameContext): Long =
    var h = 0L

    // Hash board pieces
    for (square, piece) <- context.board.pieces do
      val squareIndex = square.rank.ordinal * 8 + square.file.ordinal
      val colorIndex = if piece.color == Color.White then 0 else 1
      val pieceIndex = colorIndex * 6 + piece.pieceType.ordinal
      val randIndex = squareIndex * 12 + pieceIndex
      h ^= pieceRands(randIndex)

    // Hash side to move
    if context.turn == Color.Black then
      h ^= sideToMoveRand

    // Hash castling rights
    if context.castlingRights.whiteKingSide then
      h ^= castlingRands(0)
    if context.castlingRights.whiteQueenSide then
      h ^= castlingRands(1)
    if context.castlingRights.blackKingSide then
      h ^= castlingRands(2)
    if context.castlingRights.blackQueenSide then
      h ^= castlingRands(3)

    // Hash en-passant square
    context.enPassantSquare.foreach(sq => h ^= enPassantRands(sq.file.ordinal))

    h
