package blobovnicza

import (
	"encoding/binary"
	"fmt"
	"math"
	"math/bits"
	"strconv"
)

const firstBucketBound = uint64(32 * 1 << 10) // 32KB

func stringifyBounds(lower, upper uint64) string {
	return fmt.Sprintf("[%s:%s]",
		stringifyByteSize(lower),
		stringifyByteSize(upper),
	)
}

func stringifyByteSize(sz uint64) string {
	return strconv.FormatUint(sz, 10)
}

func bucketKeyFromBounds(upperBound uint64) []byte {
	buf := make([]byte, binary.MaxVarintLen64)

	ln := binary.PutUvarint(buf, upperBound)

	return buf[:ln]
}

func bucketForSize(sz uint64) []byte {
	return bucketKeyFromBounds(upperPowerOfTwo(sz))
}

func upperPowerOfTwo(v uint64) uint64 {
	if v <= firstBucketBound {
		return firstBucketBound
	}
	return 1 << bits.Len64(v-1)
}

func (b *Blobovnicza) itemAdded(itemSize uint64) {
	b.dataSize.Add(itemSize)
	b.itemsCount.Add(1)
	b.metrics.AddOpenBlobovniczaSize(itemSize)
	b.metrics.AddOpenBlobovniczaItems(1)
}

func (b *Blobovnicza) itemDeleted(itemSize uint64) {
	b.dataSize.Add(^(itemSize - 1))
	b.itemsCount.Add(math.MaxUint64)
	b.metrics.SubOpenBlobovniczaSize(itemSize)
	b.metrics.SubOpenBlobovniczaItems(1)
}

func (b *Blobovnicza) IsFull() bool {
	return b.dataSize.Load() >= b.fullSizeLimit
}