package main

import (
	"crypto/rand"
	"math/big"
)

// Deck — стопка карт. Индекс 0 — верх колоды (карта, которая уходит первой).
type Deck struct {
	cards []Card
}

// NewDeck создаёт полную неперетасованную колоду из 52 карт.
func NewDeck() *Deck {
	d := &Deck{cards: make([]Card, 0, 52)}
	for s := Clubs; s <= Spades; s++ {
		for r := Ace; r <= King; r++ {
			d.cards = append(d.cards, Card{Suit: s, Rank: r})
		}
	}
	return d
}

// Shuffle перемешивает колоду криптографически стойким рандомом
// (алгоритм Фишера-Йейтса).
func (d *Deck) Shuffle() {
	for i := len(d.cards) - 1; i > 0; i-- {
		jBig, err := rand.Int(rand.Reader, big.NewInt(int64(i+1)))
		if err != nil {
			// crypto/rand не должен падать в обычных условиях;
			// если это случилось — не имеет смысла продолжать игру.
			panic(err)
		}
		j := int(jBig.Int64())
		d.cards[i], d.cards[j] = d.cards[j], d.cards[i]
	}
}

// Len возвращает количество карт, оставшихся в колоде.
func (d *Deck) Len() int {
	return len(d.cards)
}

// Draw снимает верхнюю карту колоды. Второе значение — false,
// если колода пуста.
func (d *Deck) Draw() (Card, bool) {
	if len(d.cards) == 0 {
		return Card{}, false
	}
	c := d.cards[0]
	d.cards = d.cards[1:]
	return c, true
}

// DrawN снимает n верхних карт (используется при раздаче).
// Если в колоде меньше карт, чем запрошено, возвращает столько,
// сколько есть.
//
// ВАЖНО: возвращает независимую копию, а не подсрез d.cards. Раньше
// здесь было "out := d.cards[:n]" — такой подсрез имеет ту же
// вместимость (cap), что и исходный массив колоды, вплоть до самого
// его конца. Это означало, что руки РАЗНЫХ игроков после раздачи
// физически указывали на один и тот же нижележащий массив: как
// только первый игрок брал карту (append к своей руке), Go писал
// новый элемент прямо в этот общий массив — по адресу, где у
// СЛЕДУЮЩЕГО игрока лежала его собственная карта, молча её затирая.
// Тот самый баг с "одна и та же карта одновременно в сбросе и в
// комбинации на столе" — именно отсюда.
func (d *Deck) DrawN(n int) []Card {
	if n > len(d.cards) {
		n = len(d.cards)
	}
	out := make([]Card, n)
	copy(out, d.cards[:n])
	d.cards = d.cards[n:]
	return out
}
