// 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 runtime

import (
	"unsafe"
)

// listHead points to the head of an intrusive doubly-linked list.
//
// Prior to use, you must call init to store the offset of listNode fields.
//
// Every object in the list should be the same type.
type listHead struct {
	obj unsafe.Pointer

	initialized bool
	nodeOffset  uintptr
}

// init initializes the list head. off is the offset (via unsafe.Offsetof) of
// the listNode field in the objects in the list.
func (head *listHead) init(off uintptr) {
	head.initialized = true
	head.nodeOffset = off
}

// listNode is the linked list node for objects in a listHead list.
//
// listNode must be stored as a field in objects placed in the linked list. The
// offset of the field is registered via listHead.init.
//
// For example:
//
//	type foo struct {
//		val int
//
//		node listNode
//	}
//
// var fooHead listHead
// fooHead.init(unsafe.Offsetof(foo{}.node))
type listNode struct {
	prev unsafe.Pointer
	next unsafe.Pointer
}

func (head *listHead) getNode(p unsafe.Pointer) *listNode {
	if !head.initialized {
		throw("runtime: uninitialized listHead")
	}

	if p == nil {
		return nil
	}
	return (*listNode)(unsafe.Add(p, head.nodeOffset))
}

// Returns true if the list is empty.
func (head *listHead) empty() bool {
	return head.obj == nil
}

// Returns the head of the list without removing it.
func (head *listHead) head() unsafe.Pointer {
	return head.obj
}

// Push p onto the front of the list.
func (head *listHead) push(p unsafe.Pointer) {
	// p becomes the head of the list.

	// ... so p's next is the current head.
	pNode := head.getNode(p)
	pNode.next = head.obj

	// ... and the current head's prev is p.
	if head.obj != nil {
		headNode := head.getNode(head.obj)
		headNode.prev = p
	}

	head.obj = p
}

// Pop removes the head of the list.
func (head *listHead) pop() unsafe.Pointer {
	if head.obj == nil {
		return nil
	}

	// Return the head of the list.
	p := head.obj

	// ... so the new head is p's next.
	pNode := head.getNode(p)
	head.obj = pNode.next
	// p is no longer on the list. Clear next to remove unused references.
	// N.B. as the head, prev must already be nil.
	pNode.next = nil

	// ... and the new head no longer has a prev.
	if head.obj != nil {
		headNode := head.getNode(head.obj)
		headNode.prev = nil
	}

	return p
}

// Remove p from the middle of the list.
func (head *listHead) remove(p unsafe.Pointer) {
	if head.obj == p {
		// Use pop to ensure head is updated when removing the head.
		head.pop()
		return
	}

	pNode := head.getNode(p)
	prevNode := head.getNode(pNode.prev)
	nextNode := head.getNode(pNode.next)

	// Link prev to next.
	if prevNode != nil {
		prevNode.next = pNode.next
	}
	// Link next to prev.
	if nextNode != nil {
		nextNode.prev = pNode.prev
	}

	pNode.prev = nil
	pNode.next = nil
}
