// Copyright 2024 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.

//go:build go1.23

package inspector

import (
	"go/ast"
	"iter"
)

// PreorderSeq returns an iterator that visits all the
// nodes of the files supplied to New in depth-first order.
// It visits each node n before n's children.
// The complete traversal sequence is determined by ast.Inspect.
//
// The types argument, if non-empty, enables type-based
// filtering of events: only nodes whose type matches an
// element of the types slice are included in the sequence.
func (in *Inspector) PreorderSeq(types ...ast.Node) iter.Seq[ast.Node] {

	// This implementation is identical to Preorder,
	// except that it supports breaking out of the loop.

	return func(yield func(ast.Node) bool) {
		mask := maskOf(types)
		for i := 0; i < len(in.events); {
			ev := in.events[i]
			if ev.index > i {
				// push
				if ev.typ&mask != 0 {
					if !yield(ev.node) {
						break
					}
				}
				pop := ev.index
				if in.events[pop].typ&mask == 0 {
					// Subtrees do not contain types: skip them and pop.
					i = pop + 1
					continue
				}
			}
			i++
		}
	}
}

// All[N] returns an iterator over all the nodes of type N.
// N must be a pointer-to-struct type that implements ast.Node.
//
// Example:
//
//	for call := range All[*ast.CallExpr](in) { ... }
func All[N interface {
	*S
	ast.Node
}, S any](in *Inspector) iter.Seq[N] {

	// To avoid additional dynamic call overheads,
	// we duplicate rather than call the logic of PreorderSeq.

	mask := typeOf((N)(nil))
	return func(yield func(N) bool) {
		for i := 0; i < len(in.events); {
			ev := in.events[i]
			if ev.index > i {
				// push
				if ev.typ&mask != 0 {
					if !yield(ev.node.(N)) {
						break
					}
				}
				pop := ev.index
				if in.events[pop].typ&mask == 0 {
					// Subtrees do not contain types: skip them and pop.
					i = pop + 1
					continue
				}
			}
			i++
		}
	}
}
