package balancer

import "math"

// zeroVolumeDensityWeight is the ratio given to a server holding no data, so it
// still ranks below any loaded server (which has ratio >= 1) but is distinguished
// from a server with unknown capacity.
const zeroVolumeDensityWeight = 0.5

// UsedVolumeEquivalents converts the bytes a server holds into whole-volume
// equivalents (volumeBytes / volumeSizeLimit). Returns 0 when the limit is unset.
func UsedVolumeEquivalents(volumeBytes, volumeSizeLimitBytes uint64) uint64 {
	if volumeSizeLimitBytes == 0 {
		return 0
	}
	return volumeBytes / volumeSizeLimitBytes
}

// VolumeDensity is the bytes-aware capacity view balancing ranks servers by:
// used = volumeBytes / volumeSizeLimit, capacity = maxVolumeCount - used. Using
// real data (not just volume count) means an over-configured maxVolumeCount can't
// make a byte-full server look empty. capacity may be <= 0 for a server already
// past its configured slots.
func VolumeDensity(maxVolumeCount int64, volumeBytes, volumeSizeLimitBytes uint64) (capacity float64, usedVolumes uint64) {
	usedVolumes = UsedVolumeEquivalents(volumeBytes, volumeSizeLimitBytes)
	return float64(maxVolumeCount - int64(usedVolumes)), usedVolumes
}

// DensityRatio is a server's load: usedVolumes / capacity, higher = fuller. An
// empty server gets a small positive ratio so it sorts below loaded servers.
// A server with no free capacity (capacity <= 0: full, or over its configured
// slots so capacity is negative) ranks as the fullest (+Inf), so ascending
// consumers treat it as a move source and never as the emptiest target.
func DensityRatio(capacity float64, usedVolumes uint64) float64 {
	if capacity <= 0 {
		return math.Inf(1)
	}
	if usedVolumes == 0 {
		return zeroVolumeDensityWeight / capacity
	}
	return float64(usedVolumes) / capacity
}

// DensityNextRatio is a server's load after one more volume lands on it:
// (usedVolumes+1) / capacity. Used to test whether a move would overshoot. A
// server with no free capacity (capacity <= 0) ranks as the fullest (+Inf).
func DensityNextRatio(capacity float64, usedVolumes uint64) float64 {
	if capacity <= 0 {
		return math.Inf(1)
	}
	return float64(usedVolumes+1) / capacity
}
