/*
 * SPDX-FileCopyrightText: © Hypermode Inc. <hello@hypermode.com>
 * SPDX-License-Identifier: Apache-2.0
 */

package z

import (
	
	
	
	
	
	

	
)

var (
	pageSize = os.Getpagesize()
	maxKeys  = (pageSize / 16) - 1
	//nolint:unused
	oneThird = int(float64(maxKeys) / 3)
)

const (
	absoluteMax = uint64(math.MaxUint64 - 1)
	minSize     = 1 << 20
)

// Tree represents the structure for custom mmaped B+ tree.
// It supports keys in range [1, math.MaxUint64-1] and values [1, math.Uint64].
type Tree struct {
	buffer   *Buffer
	data     []byte
	nextPage uint64
	freePage uint64
	stats    TreeStats
}

func ( *Tree) () {
	// This is the root node.
	.newNode(0)
	// This acts as the rightmost pointer (all the keys are <= this key).
	.Set(absoluteMax, 0)
}

// NewTree returns an in-memory B+ tree.
func ( string) *Tree {
	const  = "tree"
	if  == "" {
		 = 
	}
	 := &Tree{buffer: NewBuffer(minSize, )}
	.Reset()
	return 
}

// NewTree returns a persistent on-disk B+ tree.
func ( string) (*Tree, error) {
	 := &Tree{}
	var  error

	// Open the buffer from disk and set it to the maximum allocated size.
	.buffer,  = NewBufferPersistent(, minSize)
	if  != nil {
		return nil, 
	}
	.buffer.offset = uint64(len(.buffer.buf))
	.data = .buffer.Bytes()

	// pageID can never be 0 if the tree has been initialized.
	 := .node(1)
	 := .pageID() != 0

	if ! {
		.nextPage = 1
		.freePage = 0
		.initRootNode()
	} else {
		.reinit()
	}

	return , nil
}

// reinit sets the internal variables of a Tree, which are normally stored
// in-memory, but are lost when loading from disk.
func ( *Tree) () {
	// Calculate t.nextPage by finding the first node whose pageID is not set.
	.nextPage = 1
	for int(.nextPage)*pageSize < len(.data) {
		 := .node(.nextPage)
		if .pageID() == 0 {
			break
		}
		.nextPage++
	}
	 := .nextPage - 1

	// Calculate t.freePage by finding the page to which no other page points.
	// This would be the head of the page linked list.
	// tailPages[i] is true if pageId i+1 is not the head of the list.
	 := make([]bool, )
	// Mark all pages containing nodes as tail pages.
	.Iterate(func( node) {
		 := .pageID() - 1
		[] = true
		// If this is a leaf node, increment the stats.
		if .isLeaf() {
			.stats.NumLeafKeys += .numKeys()
		}
	})
	// pointedPages is a list of page IDs that the tail pages point to.
	 := make([]uint64, 0)
	for ,  := range  {
		if ! {
			 := uint64() + 1
			// Skip if nextPageId = 0, as that is equivalent to null page.
			if  := .node().uint64(0);  != 0 {
				 = append(, )
			}
			.stats.NumPagesFree++
		}
	}

	// Mark all pages being pointed to as tail pages.
	for ,  := range  {
		 :=  - 1
		[] = true
	}
	// There should only be one head page left.
	for ,  := range  {
		if ! {
			 := uint64() + 1
			.freePage = 
			break
		}
	}
}

// Reset resets the tree and truncates it to maxSz.
func ( *Tree) () {
	// Tree relies on uninitialized data being zeroed out, so we need to Memclr
	// the data before using it again.
	Memclr(.buffer.buf)
	.buffer.Reset()
	.buffer.AllocateOffset(minSize)
	.data = .buffer.Bytes()
	.stats = TreeStats{}
	.nextPage = 1
	.freePage = 0
	.initRootNode()
}

// Close releases the memory used by the tree.
func ( *Tree) () error {
	if  == nil {
		return nil
	}
	return .buffer.Release()
}

type TreeStats struct {
	Allocated    int     // Derived.
	Bytes        int     // Derived.
	NumLeafKeys  int     // Calculated.
	NumPages     int     // Derived.
	NumPagesFree int     // Calculated.
	Occupancy    float64 // Derived.
	PageSize     int     // Derived.
}

// Stats returns stats about the tree.
func ( *Tree) () TreeStats {
	 := int(.nextPage - 1)
	 := TreeStats{
		Bytes:         * pageSize,
		Allocated:    len(.data),
		NumLeafKeys:  .stats.NumLeafKeys,
		NumPages:     ,
		NumPagesFree: .stats.NumPagesFree,
		PageSize:     pageSize,
	}
	.Occupancy = 100.0 * float64(.NumLeafKeys) / float64(maxKeys*)
	return 
}

// BytesToUint64Slice converts a byte slice to a uint64 slice.
func ( []byte) []uint64 {
	if len() == 0 {
		return nil
	}
	var  []uint64
	 := (*reflect.SliceHeader)(unsafe.Pointer(&))
	.Len = len() / 8
	.Cap = .Len
	.Data = uintptr(unsafe.Pointer(&[0]))
	return 
}

func ( *Tree) ( uint64) node {
	var  uint64
	if .freePage > 0 {
		 = .freePage
		.stats.NumPagesFree--
	} else {
		 = .nextPage
		.nextPage++
		 := int() * pageSize
		 :=  + pageSize
		if  > len(.data) {
			.buffer.AllocateOffset( - len(.data))
			.data = .buffer.Bytes()
		}
	}
	 := .node()
	if .freePage > 0 {
		.freePage = .uint64(0)
	}
	zeroOut()
	.setBit()
	.setAt(keyOffset(maxKeys), )
	return 
}

func getNode( []byte) node {
	return node(BytesToUint64Slice())
}

func zeroOut( []uint64) {
	for  := 0;  < len(); ++ {
		[] = 0
	}
}

func ( *Tree) ( uint64) node {
	// page does not exist
	if  == 0 {
		return nil
	}
	 := pageSize * int()
	return getNode(.data[ : +pageSize])
}

// Set sets the key-value pair in the tree.
func ( *Tree) (,  uint64) {
	if  == math.MaxUint64 ||  == 0 {
		panic("Error setting zero or MaxUint64")
	}
	 := .set(1, , )
	if .isFull() {
		 := .split(1)
		 := .newNode(.bits())
		// Re-read the root as the underlying buffer for tree might have changed during split.
		 = .node(1)
		copy([:keyOffset(maxKeys)], )
		.setNumKeys(.numKeys())

		// reset the root node.
		zeroOut([:keyOffset(maxKeys)])
		.setNumKeys(0)

		// set the pointers for left and right child in the root node.
		.set(.maxKey(), .pageID())
		.set(.maxKey(), .pageID())
	}
}

// For internal nodes, they contain <key, ptr>.
// where all entries <= key are stored in the corresponding ptr.
func ( *Tree) (, ,  uint64) node {
	 := .node()
	if .isLeaf() {
		.stats.NumLeafKeys += .set(, )
		return 
	}

	// This is an internal node.
	 := .search()
	if  >= maxKeys {
		panic("search returned index >= maxKeys")
	}
	// If no key at idx.
	if .key() == 0 {
		.setAt(keyOffset(), )
		.setNumKeys(.numKeys() + 1)
	}
	 := .node(.val())
	if  == nil {
		 = .newNode(bitLeaf)
		 = .node()
		.setAt(valOffset(), .pageID())
	}
	 = .(.pageID(), , )
	// Re-read n as the underlying buffer for tree might have changed during set.
	 = .node()
	if .isFull() {
		// Just consider the left sibling for simplicity.
		// if t.shareWithSibling(n, idx) {
		// 	return n
		// }

		 := .split(.pageID())
		// Re-read n and child as the underlying buffer for tree might have changed during split.
		 = .node()
		 = .node(.uint64(valOffset()))
		// Set child pointers in the node n.
		// Note that key for right node (nn) already exist in node n, but the
		// pointer is updated.
		.set(.maxKey(), .pageID())
		.set(.maxKey(), .pageID())
	}
	return 
}

// Get looks for key and returns the corresponding value.
// If key is not found, 0 is returned.
func ( *Tree) ( uint64) uint64 {
	if  == math.MaxUint64 ||  == 0 {
		panic("Does not support getting MaxUint64/Zero")
	}
	 := .node(1)
	return .get(, )
}

func ( *Tree) ( node,  uint64) uint64 {
	if .isLeaf() {
		return .get()
	}
	// This is internal node
	 := .search()
	if  == .numKeys() || .key() == 0 {
		return 0
	}
	 := .node(.uint64(valOffset()))
	assert( != nil)
	return .(, )
}

// DeleteBelow deletes all keys with value under ts.
func ( *Tree) ( uint64) {
	 := .node(1)
	.stats.NumLeafKeys = 0
	.compact(, )
	assert(.numKeys() >= 1)
}

func ( *Tree) ( node,  uint64) int {
	if .isLeaf() {
		 := .compact()
		.stats.NumLeafKeys += .numKeys()
		return 
	}
	// Not leaf.
	 := .numKeys()
	for  := 0;  < ; ++ {
		assert(.key() > 0)
		 := .uint64(valOffset())
		 := .node()
		if  := .(, );  == 0 &&  < -1 {
			// If no valid key is remaining we can drop this child. However, don't do that if this
			// is the max key.
			.stats.NumLeafKeys -= .numKeys()
			.setAt(0, .freePage)
			.freePage = 
			.setAt(valOffset(), 0)
			.stats.NumPagesFree++
		}
	}
	// We use ts=1 here because we want to delete all the keys whose value is 0, which means they no
	// longer have a valid page for that key.
	return .compact(1)
}

func ( *Tree) ( node,  func(node)) {
	()
	if .isLeaf() {
		return
	}
	// Explore children.
	for  := 0;  < maxKeys; ++ {
		if .key() == 0 {
			return
		}
		 := .uint64(valOffset())
		assert( > 0)

		 := .node()
		.(, )
	}
}

// Iterate iterates over the tree and executes the fn on each node.
func ( *Tree) ( func(node)) {
	 := .node(1)
	.iterate(, )
}

// IterateKV iterates through all keys and values in the tree.
// If newVal is non-zero, it will be set in the tree.
func ( *Tree) ( func(,  uint64) ( uint64)) {
	.Iterate(func( node) {
		// Only leaf nodes contain keys.
		if !.isLeaf() {
			return
		}

		for  := 0;  < .numKeys(); ++ {
			 := .key()
			 := .val()

			// A zero value here means that this is a bogus entry.
			if  == 0 {
				continue
			}

			 := (, )
			if  != 0 {
				.setAt(valOffset(), )
			}
		}
	})
}

func ( *Tree) ( node,  uint64) {
	.print()
	if .isLeaf() {
		return
	}
	 := .pageID()
	for  := 0;  < maxKeys; ++ {
		if .key() == 0 {
			return
		}
		 := .uint64(valOffset())
		 := .node()
		.(, )
	}
}

// Print iterates over the tree and prints all valid KVs.
func ( *Tree) () {
	 := .node(1)
	.print(, 0)
}

// Splits the node into two. It moves right half of the keys from the original node to a newly
// created right node. It returns the right node.
func ( *Tree) ( uint64) node {
	 := .node()
	if !.isFull() {
		panic("This should be called only when n is full")
	}

	// Create a new node nn, copy over half the keys from n, and set the parent to n's parent.
	 := .newNode(.bits())
	// Re-read n as the underlying buffer for tree might have changed during newNode.
	 = .node()
	 := [keyOffset(maxKeys/2):keyOffset(maxKeys)]
	copy(, )
	.setNumKeys(maxKeys - maxKeys/2)

	// Remove entries from node n.
	zeroOut()
	.setNumKeys(maxKeys / 2)
	return 
}

// shareWithSiblingXXX is unused for now. The idea is to move some keys to
// sibling when a node is full. But, I don't see any special benefits in our
// access pattern. It doesn't result in better occupancy ratios.
//
//nolint:unused
func ( *Tree) ( node,  int) bool {
	if  == 0 {
		return false
	}
	 := .node(.val( - 1))
	 := .numKeys()
	if  >= maxKeys/2 {
		// Sibling is already getting full.
		return false
	}

	 := .node(.val())
	// Copy over keys from right child to left child.
	 := copy([keyOffset():], [:keyOffset(oneThird)])
	 /= 2 // Considering that key-val constitute one key.
	.setNumKeys( + )

	// Update the max key in parent node n for the left sibling.
	.setAt(keyOffset(-1), .maxKey())

	// Now move keys to left for the right sibling.
	 := copy(, [keyOffset(oneThird):keyOffset(maxKeys)])
	.setNumKeys( / 2)
	zeroOut([:keyOffset(maxKeys)])
	return true
}

// Each node in the node is of size pageSize. Two kinds of nodes. Leaf nodes and internal nodes.
// Leaf nodes only contain the data. Internal nodes would contain the key and the offset to the
// child node.
// Internal node would have first entry as
// <0 offset to child>, <1000 offset>, <5000 offset>, and so on...
// Leaf nodes would just have: <key, value>, <key, value>, and so on...
// Last 16 bytes of the node are off limits.
// | pageID (8 bytes) | metaBits (1 byte) | 3 free bytes | numKeys (4 bytes) |
type node []uint64

func ( node) ( int) uint64 { return [] }

// func (n node) uint32(start int) uint32 { return *(*uint32)(unsafe.Pointer(&n[start])) }

func keyOffset( int) int          { return 2 *  }
func valOffset( int) int          { return 2* + 1 }
func ( node) () int        { return int(.uint64(valOffset(maxKeys)) & 0xFFFFFFFF) }
func ( node) () uint64      { return .uint64(keyOffset(maxKeys)) }
func ( node) ( int) uint64    { return .uint64(keyOffset()) }
func ( node) ( int) uint64    { return .uint64(valOffset()) }
func ( node) ( int) []uint64 { return [keyOffset():keyOffset(+1)] }

func ( node) ( int,  uint64) {
	[] = 
}

func ( node) ( int) {
	 := valOffset(maxKeys)
	 := []
	 &= 0xFFFFFFFF00000000
	 |= uint64()
	[] = 
}

func ( node) ( int) {
	 := .numKeys()
	assert( != maxKeys)
	// copy works despite of overlap in src and dst.
	// See https://golang.org/pkg/builtin/#copy
	copy([keyOffset(+1):keyOffset(+1)], [keyOffset():keyOffset()])
}

const (
	bitLeaf = uint64(1 << 63)
)

func ( node) ( uint64) {
	 := valOffset(maxKeys)
	 := []
	 &= 0xFFFFFFFF
	 |= 
	[] = 
}
func ( node) () uint64 {
	return .val(maxKeys) & 0xFF00000000000000
}
func ( node) () bool {
	return .bits()&bitLeaf > 0
}

// isFull checks that the node is already full.
func ( node) () bool {
	return .numKeys() == maxKeys
}

// Search returns the index of a smallest key >= k in a node.
func ( node) ( uint64) int {
	 := .numKeys()
	if  < 4 {
		for  := 0;  < ; ++ {
			if  := .key();  >=  {
				return 
			}
		}
		return 
	}
	return int(simd.Search([:2*], ))
	// lo, hi := 0, N
	// // Reduce the search space using binary seach and then do linear search.
	// for hi-lo > 32 {
	// 	mid := (hi + lo) / 2
	// 	km := n.key(mid)
	// 	if k == km {
	// 		return mid
	// 	}
	// 	if k > km {
	// 		// key is greater than the key at mid, so move right.
	// 		lo = mid + 1
	// 	} else {
	// 		// else move left.
	// 		hi = mid
	// 	}
	// }
	// for i := lo; i <= hi; i++ {
	// 	if ki := n.key(i); ki >= k {
	// 		return i
	// 	}
	// }
	// return N
}
func ( node) () uint64 {
	 := .numKeys()
	// idx points to the first key which is zero.
	if  > 0 {
		--
	}
	return .key()
}

// compacts the node i.e., remove all the kvs with value < lo. It returns the remaining number of
// keys.
func ( node) ( uint64) int {
	 := .numKeys()
	 := .maxKey()
	var ,  int
	for  = 0;  < ; ++ {
		if .val() <  && .key() <  {
			// Skip over this key. Don't copy it.
			continue
		}
		// Valid data. Copy it from right to left. Advance left.
		if  !=  {
			copy(.data(), .data())
		}
		++
	}
	// zero out rest of the kv pairs.
	zeroOut([keyOffset():keyOffset()])
	.setNumKeys()

	// If the only key we have is the max key, and its value is less than lo, then we can indicate
	// to the caller by returning a zero that it's OK to drop the node.
	if  == 1 && .key(0) ==  && .val(0) <  {
		return 0
	}
	return 
}

func ( node) ( uint64) uint64 {
	 := .search()
	// key is not found
	if  == .numKeys() {
		return 0
	}
	if  := .key();  ==  {
		return .val()
	}
	return 0
}

// set returns true if it added a new key.
func ( node) (,  uint64) ( int) {
	 := .search()
	 := .key()
	if .numKeys() == maxKeys {
		// This happens during split of non-root node, when we are updating the child pointer of
		// right node. Hence, the key should already exist.
		assert( == )
	}
	if  >  {
		// Found the first entry which is greater than k. So, we need to fit k
		// just before it. For that, we should move the rest of the data in the
		// node to the right to make space for k.
		.moveRight()
	}
	// If the k does not exist already, increment the number of keys.
	if  !=  {
		.setNumKeys(.numKeys() + 1)
		 = 1
	}
	if  == 0 ||  >=  {
		.setAt(keyOffset(), )
		.setAt(valOffset(), )
		return
	}
	panic("shouldn't reach here")
}

func ( node) ( func(node, int)) {
	for  := 0;  < maxKeys; ++ {
		if  := .key();  > 0 {
			(, )
		} else {
			break
		}
	}
}

func ( node) ( uint64) {
	var  []string
	.iterate(func( node,  int) {
		 = append(, fmt.Sprintf("%d", .key()))
	})
	if len() > 8 {
		copy([4:], [len()-4:])
		[3] = "..."
		 = [:8]
	}
	fmt.Printf("%d Child of: %d num keys: %d keys: %s\n",
		.pageID(), , .numKeys(), strings.Join(, " "))
}