package util

import (
	"math"
	"math/rand"
	"time"
)

type RNG struct {
	seed int64
	rand *rand.Rand
}

func NewRNG() *RNG {
	rng := RNG{}

	// Set the seed to the current time. This can be updated later by the user.
	rng.seed = time.Now().UTC().UnixNano()
	rng.rand = rand.New(rand.NewSource(rng.seed))

	return &rng
}

func (rng *RNG) GetSeed() int64 {
	return rng.seed
}

func (rng *RNG) SetSeed(seed int64) {
	rng.seed = seed
}

func (rng *RNG) Uniform() float64 {
	return rng.rand.Float64()
}

func (rng *RNG) UniformRange(a, b float64) float64 {
	return a + rng.Uniform()*(b-a)
}

func (rng *RNG) Normal(mean, stddev float64) float64 {

	var r, x float64

	for r >= 1 || r == 0 {
		x = rng.UniformRange(-1.0, 1.0)
		y := rng.UniformRange(-1.0, 1.0)
		r = x*x + y*y
	}

	result := x * math.Sqrt(-2*math.Log(r)/r)

	return mean + stddev*result

}

func (rng *RNG) Percentage() int {
	return rng.rand.Intn(100)
}

func (rng *RNG) Range(min, max int) int {
	if min == max {
		return min
	} else {
		return rng.rand.Intn(max-min) + min
	}
}

func (rng *RNG) RangeNegative(min, max int) int {
	if min == max {
		return min
	} else {
		return rng.rand.Intn(max-min+1) + min
	}
}

func (rng *RNG) GetWeightedEntity(values map[int]int) int {
	// First up, get the total weight value from the gamemap
	totalWeight := 0
	for weight := range values {
		totalWeight += weight
	}

	// Next, get a random integer in the range of the total weight
	r := rng.Range(0, totalWeight)

	for weight, value := range values {
		r -= value
		if r <= 0 {
			return weight
		}
	}

	return -1
}
