package engine

import (
	"math/rand"

	"gitea.iliadenisov.ru/developer/scrabble-solver/rules"
)

// blankTile marks a blank tile in a hand or in the bag, matching the
// scrabble-solver convention (selfplay) so a hand of these bytes interoperates
// with the solver's rack helpers.
const blankTile byte = 0xff

// Bag is the shuffled draw pile for one game. Unlike the solver's self-play bag
// it supports returning tiles, which an exchange needs. It is seeded once, so a
// game's draws are reproducible from its seed and the sequence of operations.
// Bag is not safe for concurrent use; the owning Game serialises access.
type Bag struct {
	tiles []byte
	rng   *rand.Rand
}

// NewBag fills a bag from the ruleset's tile counts and blanks and shuffles it
// with seed. Letters are stored as alphabet-index bytes and blanks as blankTile.
func NewBag(rs *rules.Ruleset, seed int64) *Bag {
	var tiles []byte
	for i, n := range rs.Counts {
		for range n {
			tiles = append(tiles, byte(i))
		}
	}
	for range rs.Blanks {
		tiles = append(tiles, blankTile)
	}
	b := &Bag{tiles: tiles, rng: rand.New(rand.NewSource(seed))}
	b.shuffle()
	return b
}

// Len returns the number of tiles left in the bag.
func (b *Bag) Len() int { return len(b.tiles) }

// Draw removes up to n tiles from the bag and returns them in a fresh slice.
// Drawing more than remain returns all of them; drawing from an empty bag
// returns an empty slice.
func (b *Bag) Draw(n int) []byte {
	if n > len(b.tiles) {
		n = len(b.tiles)
	}
	out := make([]byte, n)
	copy(out, b.tiles[len(b.tiles)-n:])
	b.tiles = b.tiles[:len(b.tiles)-n]
	return out
}

// Return puts tiles back into the bag and reshuffles, as when a player exchanges
// tiles. The tiles must use the same encoding as Draw (alphabet indices and
// blankTile).
func (b *Bag) Return(tiles []byte) {
	b.tiles = append(b.tiles, tiles...)
	b.shuffle()
}

// shuffle randomises the remaining tiles with the bag's own RNG, keeping draws
// deterministic for a given seed and sequence of operations.
func (b *Bag) shuffle() {
	b.rng.Shuffle(len(b.tiles), func(i, j int) { b.tiles[i], b.tiles[j] = b.tiles[j], b.tiles[i] })
}
