// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package token

// tree is a self-balancing AVL tree; see
// Lewis & Denenberg, Data Structures and Their Algorithms.
//
// An AVL tree is a binary tree in which the difference between the
// heights of a node's two subtrees--the node's "balance factor"--is
// at most one. It is more strictly balanced than a red/black tree,
// and thus favors lookups at the expense of updates, which is the
// appropriate trade-off for FileSet.
//
// Insertion at a node may cause its ancestors' balance factors to
// temporarily reach ±2, requiring rebalancing of each such ancestor
// by a rotation.
//
// Each key is the pos-end range of a single File.
// All Files in the tree must have disjoint ranges.
//
// The implementation is simplified from Russ Cox's github.com/rsc/omap.

import (
	
	
)

// A tree is a tree-based ordered map:
// each value is a *File, keyed by its Pos range.
// All map entries cover disjoint ranges.
//
// The zero value of tree is an empty map ready to use.
type tree struct {
	root *node
}

type node struct {
	// We use the notation (parent left right) in many comments.
	parent  *node
	left    *node
	right   *node
	file    *File
	key     key   // = file.key(), but improves locality (25% faster)
	balance int32 // at most ±2
	height  int32
}

// A key represents the Pos range of a File.
type key struct{ start, end int }

func ( *File) () key {
	return key{.base, .base + .size}
}

// compareKey reports whether x is before y (-1),
// after y (+1), or overlapping y (0).
// This is a total order so long as all
// files in the tree have disjoint ranges.
//
// All files are separated by at least one unit.
// This allows us to use strict < comparisons.
// Use key{p, p} to search for a zero-width position
// even at the start or end of a file.
func compareKey(,  key) int {
	switch {
	case .end < .start:
		return -1
	case .end < .start:
		return +1
	}
	return 0
}

// check asserts that each node's height, subtree, and parent link is
// correct.
func ( *node) ( *node) {
	const  = false
	if  {
		if  == nil {
			return
		}
		if .parent !=  {
			panic("bad parent")
		}
		.left.()
		.right.()
		.checkBalance()
	}
}

func ( *node) () {
	,  := .left.safeHeight(), .right.safeHeight()
	 :=  - 
	if  != .balance {
		panic("bad node.balance")
	}
	if !(-2 <=  &&  <= +2) {
		panic(fmt.Sprintf("node.balance out of range: %d", ))
	}
	 := 1 + max(, )
	if  != .height {
		panic("bad node.height")
	}
}

// locate returns a pointer to the variable that holds the node
// identified by k, along with its parent, if any. If the key is not
// present, it returns a pointer to the node where the key should be
// inserted by a subsequent call to [tree.set].
func ( *tree) ( key) ( **node,  *node) {
	,  := &.root, .root
	for  != nil {
		 := compareKey(, .key)
		if  < 0 {
			, ,  = &.left, .left, 
		} else if  > 0 {
			, ,  = &.right, .right, 
		} else {
			break
		}
	}
	return , 
}

// all returns an iterator over the tree t.
// If t is modified during the iteration,
// some files may not be visited.
// No file will be visited multiple times.
func ( *tree) () iter.Seq[*File] {
	return func( func(*File) bool) {
		if  == nil {
			return
		}
		 := .root
		if  != nil {
			for .left != nil {
				 = .left
			}
		}
		for  != nil && (.file) {
			if .height >= 0 {
				// still in tree
				 = .next()
			} else {
				// deleted
				 = .nextAfter(.locate(.key))
			}
		}
	}
}

// nextAfter returns the node in the key sequence following
// (pos, parent), a result pair from [tree.locate].
func ( *tree) ( **node,  *node) *node {
	switch {
	case * != nil:
		return (*).next()
	case  == nil:
		return nil
	case  == &.left:
		return 
	default:
		return .next()
	}
}

func ( *node) () *node {
	if .right == nil {
		for .parent != nil && .parent.right ==  {
			 = .parent
		}
		return .parent
	}
	 = .right
	for .left != nil {
		 = .left
	}
	return 
}

func ( *tree) ( *node) {
	.root = 
	if  != nil {
		.parent = nil
	}
}

func ( *node) ( *node) {
	.left = 
	if  != nil {
		.parent = 
	}
}

func ( *node) ( *node) {
	.right = 
	if  != nil {
		.parent = 
	}
}

func ( *node) () int32 {
	if  == nil {
		return -1
	}
	return .height
}

func ( *node) () {
	,  := .left.safeHeight(), .right.safeHeight()
	.height = max(, ) + 1
	.balance =  - 
}

func ( *tree) (, ,  *node) {
	switch {
	case  == nil:
		if .root !=  {
			panic("corrupt tree")
		}
		.setRoot()
	case .left == :
		.setLeft()
	case .right == :
		.setRight()
	default:
		panic("corrupt tree")
	}
}

// rebalanceUp visits each excessively unbalanced ancestor
// of x, restoring balance by rotating it.
//
// x is a node that has just been mutated, and so the height and
// balance of x and its ancestors may be stale, but the children of x
// must be in a valid state.
func ( *tree) ( *node) {
	for  != nil {
		 := .height
		.update()
		switch .balance {
		case -2:
			if .left.balance == 1 {
				.rotateLeft(.left)
			}
			 = .rotateRight()

		case +2:
			if .right.balance == -1 {
				.rotateRight(.right)
			}
			 = .rotateLeft()
		}
		if .height ==  {
			// x's height has not changed, so the height
			// and balance of its ancestors have not changed;
			// no further rebalancing is required.
			return
		}
		 = .parent
	}
}

// rotateRight rotates the subtree rooted at node y.
// turning (y (x a b) c) into (x a (y b c)).
func ( *tree) ( *node) *node {
	// p -> (y (x a b) c)
	 := .parent
	 := .left
	 := .right

	.checkBalance()
	.checkBalance()

	.setRight()
	.setLeft()
	.replaceChild(, , )

	.update()
	.update()
	return 
}

// rotateLeft rotates the subtree rooted at node x.
// turning (x a (y b c)) into (y (x a b) c).
func ( *tree) ( *node) *node {
	// p -> (x a (y b c))
	 := .parent
	 := .right
	 := .left

	.checkBalance()
	.checkBalance()

	.setLeft()
	.setRight()
	.replaceChild(, , )

	.update()
	.update()
	return 
}

// add inserts file into the tree, if not present.
// It panics if file overlaps with another.
func ( *tree) ( *File) {
	,  := .locate(.key())
	if * == nil {
		.set(, , ) // missing; insert
		return
	}
	if  := (*).file;  !=  {
		panic(fmt.Sprintf("file %s (%d-%d) overlaps with file %s (%d-%d)",
			.Name(), .Base(), .Base()+.Size(),
			.Name(), .Base(), .Base()+.Size()))
	}
}

// set updates the existing node at (pos, parent) if present, or
// inserts a new node if not, so that it refers to file.
func ( *tree) ( *File,  **node,  *node) {
	if  := *;  != nil {
		// This code path isn't currently needed
		// because FileSet never updates an existing entry.
		// Remove this assertion if things change.
		if true {
			panic("unreachable according to current FileSet requirements")
		}
		.file = 
		return
	}
	 := &node{file: , key: .key(), parent: , height: -1}
	* = 
	.rebalanceUp()
}

// delete deletes the node at pos.
func ( *tree) ( **node) {
	.root.check(nil)

	 := *
	switch {
	case  == nil:
		// This code path isn't currently needed because FileSet
		// only calls delete after a positive locate.
		// Remove this assertion if things change.
		if true {
			panic("unreachable according to current FileSet requirements")
		}
		return

	case .left == nil:
		if * = .right; * != nil {
			(*).parent = .parent
		}
		.rebalanceUp(.parent)

	case .right == nil:
		* = .left
		.left.parent = .parent
		.rebalanceUp(.parent)

	default:
		.deleteSwap()
	}

	.balance = -100
	.parent = nil
	.left = nil
	.right = nil
	.height = -1
	.root.check(nil)
}

// deleteSwap deletes a node that has two children by replacing
// it by its in-order successor, then triggers a rebalance.
func ( *tree) ( **node) {
	 := *
	 := .deleteMin(&.right)

	* = 
	 := .parent // lowest potentially unbalanced node
	if  ==  {
		 =  // (x a (z nil b)) -> (z a b)
	}
	.parent = .parent
	.height = .height
	.balance = .balance
	.setLeft(.left)
	.setRight(.right)

	.rebalanceUp()
}

// deleteMin updates the subtree rooted at *zpos to delete its minimum
// (leftmost) element, which may be *zpos itself. It returns the
// deleted node.
func ( *tree) ( **node) ( *node) {
	for (*).left != nil {
		 = &(*).left
	}
	 = *
	* = .right
	if * != nil {
		(*).parent = .parent
	}
	return 
}