import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

public class StatoImmutabileAtomico {
    record Stato(int disponibili, int prenotati) {
        Stato {
            if (disponibili < 0 || prenotati < 0 || disponibili + prenotati != 5) {
                throw new IllegalArgumentException("Stato non valido");
            }
        }
    }

    private final AtomicReference<Stato> stato =
            new AtomicReference<>(new Stato(5, 0));

    public boolean prenota() {
        while (true) {
            Stato prima = stato.get();
            if (prima.disponibili() == 0) {
                return false;
            }
            Stato dopo = new Stato(prima.disponibili() - 1, prima.prenotati() + 1);
            if (stato.compareAndSet(prima, dopo)) {
                return true;
            }
        }
    }

    public Stato fotografia() {
        return stato.get();
    }

    public static void main(String[] args) throws InterruptedException {
        StatoImmutabileAtomico catalogo = new StatoImmutabileAtomico();
        List<Thread> lettori = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            lettori.add(Thread.ofVirtual().start(catalogo::prenota));
        }
        for (Thread lettore : lettori) {
            lettore.join();
        }
        Stato risultato = catalogo.fotografia();
        if (risultato.disponibili() != 0 || risultato.prenotati() != 5) {
            throw new AssertionError("Invariante dello stato violata");
        }
        System.out.println("Disponibili: " + risultato.disponibili()
                + ", prenotati: " + risultato.prenotati());
    }
}
