package peer

type dupCheck struct {
	bitSet
	head        int
	tail        int
	headCounter uint64
	tailCounter uint64 // Also next expected counter value.
}

func newDupCheck(headCounter uint64) *dupCheck {
	return &dupCheck{
		headCounter: headCounter,
		tailCounter: headCounter + 1,
		tail:        1,
	}
}

func (dc *dupCheck) IsDup(counter uint64) bool {

	// Before head => it's late, say it's a dup.
	if counter < dc.headCounter {
		return true
	}

	// It's within the counter bounds.
	if counter < dc.tailCounter {
		index := (int(counter-dc.headCounter) + dc.head) % bitSetSize
		if dc.Get(index) {
			return true
		}

		dc.Set(index)
		return false
	}

	// It's more than 1 beyond the tail.
	delta := counter - dc.tailCounter

	// Full clear.
	if delta >= bitSetSize-1 {
		dc.ClearAll()
		dc.Set(0)

		dc.tail = 1
		dc.head = 2
		dc.tailCounter = counter + 1
		dc.headCounter = dc.tailCounter - bitSetSize + 1

		return false
	}

	// Clear if necessary.
	for range delta {
		dc.put(false)
	}

	dc.put(true)
	return false
}

func (dc *dupCheck) put(set bool) {
	if set {
		dc.Set(dc.tail)
	} else {
		dc.Clear(dc.tail)
	}

	dc.tail = (dc.tail + 1) % bitSetSize
	dc.tailCounter++

	if dc.head == dc.tail {
		dc.head = (dc.head + 1) % bitSetSize
		dc.headCounter++
	}
}
