package cuckoo

// An entry is a key-value pair.
type entry[K, V any] struct {
	key   K
	value V
}

type slot[K, V any] struct {
	entry[K, V]
	occupied bool
}

type subtable[K, V any] struct {
	hash           Hash[K]
	slots          []slot[K, V]
	capacity, size uint64
	compare        EqualFunc[K]
}

// location determines where in the subtable a certain key would be placed. If
// the capacity is 0, this will panic.
func (t *subtable[K, V]) location(key K) uint64 {
	return t.hash(key) % t.capacity
}

func (t *subtable[K, V]) get(key K) (value V, found bool) {
	if t.capacity == 0 {
		return
	}

	slot := t.slots[t.location(key)]
	return slot.value, slot.occupied && t.compare(slot.key, key)
}

func (t *subtable[K, V]) drop(key K) (occupied bool) {
	if t.capacity == 0 {
		return
	}

	slot := &t.slots[t.location(key)]

	if slot.occupied && t.compare(slot.key, key) {
		slot.occupied = false
		t.size--
		return true
	}

	return false
}

func (t *subtable[K, V]) resized(capacity uint64) *subtable[K, V] {
	return &subtable[K, V]{
		slots:    make([]slot[K, V], capacity),
		capacity: capacity,
		hash:     t.hash,
		compare:  t.compare,
	}
}

func (t *subtable[K, V]) update(key K, value V) (updated bool) {
	if t.capacity == 0 {
		return
	}

	slot := &t.slots[t.location(key)]

	if slot.occupied && t.compare(slot.key, key) {
		slot.value = value
		return true
	}

	return false
}

func (t *subtable[K, V]) insert(insertion entry[K, V]) (evicted entry[K, V], eviction bool) {
	if t.capacity == 0 {
		return insertion, true
	}

	slot := &t.slots[t.location(insertion.key)]

	if !slot.occupied {
		slot.entry = insertion
		slot.occupied = true
		t.size++
		return
	}

	if t.compare(slot.key, insertion.key) {
		slot.value = insertion.value
		return
	}

	insertion, slot.entry = slot.entry, insertion
	return insertion, true
}

func newSubtable[K, V any](capacity uint64, hash Hash[K], compare EqualFunc[K]) *subtable[K, V] {
	return &subtable[K, V]{
		hash:     hash,
		capacity: capacity,
		compare:  compare,
		size:     0,
		slots:    make([]slot[K, V], capacity),
	}
}
