// Copyright (c) 2026 The elk-go Authors.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0.
//
// SPDX-License-Identifier: EPL-2.0

package javacompat

// Comparator has the contract of java.util.Comparator.compare: negative when
// a is less than b, zero when they compare equal, and positive otherwise.
type Comparator[ any] func(a, b ) int

// PriorityQueue is a clean-room binary min-heap whose tie behavior is pinned by
// elkjs oracle vectors. Java does not define FIFO ordering for equal elements,
// but ELK observes the heap-array order, so left-child and strict-comparison
// choices are part of this compatibility type's tested behavior.
type PriorityQueue[ any] struct {
	heap    []
	compare Comparator[]
}

// NewPriorityQueue returns an empty Java-compatible min-priority queue.
func [ any]( Comparator[]) *PriorityQueue[] {
	if  == nil {
		panic("javacompat.NewPriorityQueue: nil comparator")
	}
	return &PriorityQueue[]{compare: }
}

// NewPriorityQueueCapacity is NewPriorityQueue with a capacity hint. Java's
// growth policy does not affect heap order, but avoiding reallocations is handy
// for the larger layered-layout queues.
func [ any]( int,  Comparator[]) *PriorityQueue[] {
	if  < 0 {
		panic("javacompat.NewPriorityQueueCapacity: negative capacity")
	}
	 := NewPriorityQueue()
	.heap = make([], 0, )
	return 
}

// Len returns the number of queued elements.
func ( *PriorityQueue[]) () int { return len(.heap) }

// IsEmpty reports whether the queue is empty.
func ( *PriorityQueue[]) () bool { return len(.heap) == 0 }

// Add inserts item. Java's add and offer have identical heap behavior.
func ( *PriorityQueue[]) ( ) {
	.Offer()
}

// Offer inserts item and returns true, matching PriorityQueue.offer.
func ( *PriorityQueue[]) ( ) bool {
	 := len(.heap)
	.heap = append(.heap, )
	.siftUp(, )
	return true
}

// Peek returns the least element without removing it.
func ( *PriorityQueue[]) () (, bool) {
	if len(.heap) == 0 {
		var  
		return , false
	}
	return .heap[0], true
}

// Poll removes and returns the least element.
func ( *PriorityQueue[]) () (, bool) {
	 := len(.heap)
	if  == 0 {
		var  
		return , false
	}
	 := .heap[0]
	 :=  - 1
	 := .heap[]
	var  
	.heap[] = 
	.heap = .heap[:]
	if  != 0 {
		.siftDown(0, )
	}
	return , true
}

// RemoveFirst removes the first element in the queue's internal array for
// which match returns true. This is PriorityQueue.remove(Object) with equality
// supplied explicitly, which also supports non-comparable Go values.
func ( *PriorityQueue[]) ( func() bool) bool {
	if  == nil {
		panic("javacompat.PriorityQueue.RemoveFirst: nil predicate")
	}
	for ,  := range .heap {
		if () {
			.removeAt()
			return true
		}
	}
	return false
}

// Clear removes every element while releasing references held by the backing
// slice.
func ( *PriorityQueue[]) () {
	clear(.heap)
	.heap = .heap[:0]
}

// Elements returns a copy in PriorityQueue iterator order (the heap-array
// order). This order is deliberately not sorted, just like Java's iterator.
func ( *PriorityQueue[]) () [] {
	return append([](nil), .heap...)
}

func ( *PriorityQueue[]) ( int,  ) int {
	.heap[] = 
	for  > 0 {
		 := ( - 1) / 2
		if .compare(.heap[], .heap[]) >= 0 {
			break
		}
		.heap[], .heap[] = .heap[], .heap[]
		 = 
	}
	return 
}

func ( *PriorityQueue[]) ( int,  ) int {
	.heap[] = 
	for {
		 := *2 + 1
		if  >= len(.heap) {
			return 
		}
		 := 
		 :=  + 1
		if  < len(.heap) && .compare(.heap[], .heap[]) > 0 {
			 = 
		}
		if .compare(.heap[], .heap[]) <= 0 {
			return 
		}
		.heap[], .heap[] = .heap[], .heap[]
		 = 
	}
}

func ( *PriorityQueue[]) ( int) {
	 := len(.heap) - 1
	if  ==  {
		var  
		.heap[] = 
		.heap = .heap[:]
		return
	}
	 := .heap[]
	var  
	.heap[] = 
	.heap = .heap[:]
	if .siftDown(, ) ==  {
		.siftUp(, )
	}
}