// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package utils

import (
	"math"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestBitmap(t *testing.T) {
	b := NewBitmap[uint32](153)
	require.Equal(t, 4, cap(b.bits)) // rounded up to next power of 2, i. e. 256
	require.Equal(t, 4, len(b.bits))

	e := make([]uint64, 4)
	require.Equal(t, e, b.bits)

	b.Set(666666)
	require.True(t, b.IsSet(666666))
	require.True(t, b.IsSet(666666+256)) // aliased
	require.True(t, b.IsSet(666666-256)) // aliased
	require.False(t, b.IsSet(666666-1))
	require.False(t, b.IsSet(666666+1))

	copy(e, b.bits)
	b.Set(42) // same as above - aliased
	require.Equal(t, e, b.bits)

	// same slot range
	b.SetRange(24, 63)
	// different slot range
	b.SetRange(64, 240)
	require.False(t, b.IsSet(241))
	require.False(t, b.IsSet(23))
	require.True(t, b.IsSet(24))
	require.True(t, b.IsSet(240))
	e[0] = 0xFFFF_FFFF_FF00_0000
	e[1] = ^uint64(0)
	e[2] = ^uint64(0)
	e[3] = 0x0001_FFFF_FFFF_FFFF
	require.Equal(t, e, b.bits)

	b.Clear(6700) // aliases to 44
	e[0] = 0xFFFF_EFFF_FF00_0000
	require.Equal(t, e, b.bits)
	require.False(t, b.IsSet(44))

	// same slot range
	b.ClearRange(24, 34)
	e[0] = 0xFFFF_EFF8_0000_0000
	require.Equal(t, e, b.bits)
	require.False(t, b.IsSet(24))
	require.True(t, b.IsSet(35))

	// different slot range
	b.ClearRange(95, 234)
	e[1] = 0x0000_0000_7FFF_FFFF
	e[2] = 0x0
	e[3] = 0x0001_F800_0000_0000
	require.Equal(t, e, b.bits)
	require.True(t, b.IsSet(94))
	require.False(t, b.IsSet(95))
	require.False(t, b.IsSet(234))
	require.True(t, b.IsSet(235))

	// set large range
	b.SetRange(0, 1000)
	e[0] = 0xFFFF_FFFF_FFFF_FFFF
	e[1] = 0xFFFF_FFFF_FFFF_FFFF
	e[2] = 0xFFFF_FFFF_FFFF_FFFF
	e[3] = 0xFFFF_FFFF_FFFF_FFFF
	require.Equal(t, e, b.bits)

	// clear large range
	b.ClearRange(0, 1000)
	e[0] = 0x0000_0000_0000_0000
	e[1] = 0x0000_0000_0000_0000
	e[2] = 0x0000_0000_0000_0000
	e[3] = 0x0000_0000_0000_0000
	require.Equal(t, e, b.bits)

	// large range changes touch each word once
	sm, ls, rs, lo, ro := b.getSlotsAndOffsets(0, math.MaxUint32)
	require.Equal(t, []int{3, 0, 4, 0, 0}, []int{sm, ls, rs, lo, ro})
}
