package mox

import (
	cryptorand "crypto/rand"
	"encoding/binary"
	"fmt"
	mathrand "math/rand"
	"sync"
)

type rand struct {
	rand *mathrand.Rand
	sync.Mutex
}

// NewPseudoRand returns a new PRNG seeded with random bytes from crypto/rand. Its
// functions can be called concurrently.
func NewPseudoRand() *rand {
	return &rand{rand: mathrand.New(mathrand.NewSource(CryptoRandInt()))}
}

func (r *rand) Float64() float64 {
	r.Lock()
	defer r.Unlock()
	return r.rand.Float64()
}

func (r *rand) Intn(n int) int {
	r.Lock()
	defer r.Unlock()
	return r.rand.Intn(n)
}

func (r *rand) Read(buf []byte) (int, error) {
	r.Lock()
	defer r.Unlock()
	return r.rand.Read(buf)
}

// CryptoRandInt returns a cryptographically random number.
func CryptoRandInt() int64 {
	buf := make([]byte, 8)
	_, err := cryptorand.Read(buf)
	if err != nil {
		panic(fmt.Errorf("reading random bytes: %v", err))
	}
	return int64(binary.LittleEndian.Uint64(buf))
}