package placement

import (
	
	
	
	
	

	
	
	
)

const optimizerSpatialIndexMinNodes = 96

// optimizerSpatialEntry retains graph order separately from the X ordering
// used by the interval tree. Candidate queries sort the small result set back
// into graph order before running the legacy directional overlap predicate.
type optimizerSpatialEntry struct {
	graphIndex int
	left       float64
	top        float64
	right      float64
	bottom     float64
}

type optimizerOccupancyEntry struct {
	graphIndex int
	generation uint64
}

// optimizerSpatialIndex is rebuilt once for each node considered by the sized
// optimizer. Candidate evaluation may move that node repeatedly, but all other
// boxes remain fixed; queries skip the moving node by identity. This replaces
// the candidate-by-graph scans without requiring updates for every trial point.
type optimizerSpatialIndex struct {
	graph           *layoutgraph.Graph
	nodeCount       int
	occupancyUsable bool
	spatialUsable   bool
	entries         []optimizerSpatialEntry
	maxRight        []float64
	candidates      []int

	occupied            map[geo.Point]optimizerOccupancyEntry
	occupancyGeneration uint64
}

func ( *sizedOptimizer) ( *limits.OptimizationWorkGuard) error {
	if  == nil || .g == nil {
		return fmt.Errorf("TALA %s spatial index requires an optimizer graph", .Location())
	}
	return .spatialIndex.rebuild(.g, )
}

func ( *sizedOptimizer) ( *geo.Point,  *limits.OptimizationWorkGuard) (*layoutgraph.Node, bool, error) {
	return .spatialIndex.isOccupied(.g, , )
}

func ( *sizedOptimizer) ( *layoutgraph.Node,  *geo.Point,  []*layoutgraph.Node,  *limits.OptimizationWorkGuard) (bool, error) {
	return .spatialIndex.doesOverlap(, , , )
}

func ( *sizedOptimizer) ( *layoutgraph.Node) bool {
	return  != nil && (.IsContainer() || .IsClusterVessel() || .g.Sequences[] != nil)
}

func ( *sizedOptimizer) ( *layoutgraph.Node,  *geo.Point,  *limits.OptimizationWorkGuard) (bool, error) {
	if  == nil || .Graph == nil ||  == nil {
		return false, fmt.Errorf("TALA %s movement check requires a node, graph, and point", .Location())
	}
	if nonNilEquals(.TopLeft, ) {
		return true, nil
	}
	// Candidate scoring moves descendants together with a container, cluster, or
	// sequence vessel. Their indexed boxes would become stale after the first
	// trial point, so retain the legacy scan for those comparatively rare nodes.
	if .nodeMayMoveDescendants() {
		return optimizerCanMove(, , true, )
	}
	, ,  := .indexedIsOccupied(, )
	if  != nil ||  {
		return ! &&  == nil, 
	}
	,  := .indexedDoesOverlap(, , nil, )
	return ! &&  == nil, 
}

func optimizerIndexFinite( float64) bool {
	return !math.IsNaN() && !math.IsInf(, 0)
}

func optimizerIndexFiniteBox( *layoutgraph.Node) bool {
	if  == nil || .TopLeft == nil || .Width < 0 || .Height < 0 {
		return false
	}
	return optimizerIndexFinite(.TopLeft.X) && optimizerIndexFinite(.TopLeft.Y) &&
		optimizerIndexFinite(.Width) && optimizerIndexFinite(.Height) &&
		optimizerIndexFinite(.TopLeft.X+.Width) && optimizerIndexFinite(.TopLeft.Y+.Height)
}

func ( *optimizerSpatialIndex) ( *layoutgraph.Graph,  *limits.OptimizationWorkGuard) error {
	if  == nil {
		return fmt.Errorf("TALA %s spatial index requires a graph", .Location())
	}
	if len(.Nodes) > limits.MaxEngineNodes {
		return fmt.Errorf("TALA %s spatial index node count exceeds limit %d", .Location(), limits.MaxEngineNodes)
	}

	.graph = 
	.nodeCount = len(.Nodes)
	.occupancyUsable = true
	.spatialUsable = len(.Nodes) >= optimizerSpatialIndexMinNodes
	.entries = .entries[:0]
	.candidates = .candidates[:0]

	.occupancyGeneration++
	// Avoid clearing map buckets on every optimizer node while bounding stale
	// coordinate retention when accepted moves continually introduce new keys.
	 := max(64, 4*len(.Nodes))
	if .occupancyGeneration == 0 || len(.occupied) >  {
		clear(.occupied)
		.occupancyGeneration = 1
	}
	if .occupied == nil {
		.occupied = make(map[geo.Point]optimizerOccupancyEntry, len(.Nodes))
	}

	for ,  := range .Nodes {
		if  := .Step();  != nil {
			return 
		}
		if  == nil {
			return fmt.Errorf("TALA %s found a nil graph node", .Location())
		}
		if .TopLeft == nil {
			continue
		}
		if !optimizerIndexFinite(.TopLeft.X) || !optimizerIndexFinite(.TopLeft.Y) {
			.occupancyUsable = false
			.spatialUsable = false
			continue
		}
		 := *.TopLeft
		,  := .occupied[]
		if ! || .generation != .occupancyGeneration {
			.occupied[] = optimizerOccupancyEntry{
				graphIndex: ,
				generation: .occupancyGeneration,
			}
		}

		if !.spatialUsable {
			continue
		}
		if !optimizerIndexFiniteBox() {
			// The legacy predicates have intentionally unusual behavior for NaN,
			// infinities, and negative dimensions. Retain that behavior by using
			// their full scan rather than forcing those values into an ordering.
			.spatialUsable = false
			continue
		}
		.entries = append(.entries, optimizerSpatialEntry{
			graphIndex: ,
			left:       .TopLeft.X,
			top:        .TopLeft.Y,
			right:      .TopLeft.X + .Width,
			bottom:     .TopLeft.Y + .Height,
		})
	}

	if !.spatialUsable || len(.entries) == 0 {
		return .Finish()
	}
	if  := .AddSort(len(.entries));  != nil {
		return 
	}
	slices.SortStableFunc(.entries, func(,  optimizerSpatialEntry) int {
		if .left != .left {
			switch {
			case .left < .left:
				return -1
			case .left < .left:
				return 1
			default:
				return 0
			}
		}
		return cmp.Compare(.graphIndex, .graphIndex)
	})

	 := 4 * len(.entries)
	if cap(.maxRight) <  {
		.maxRight = make([]float64, )
	} else {
		.maxRight = .maxRight[:]
	}
	.buildMaxRight(1, 0, len(.entries))
	return .Finish()
}

func ( *optimizerSpatialIndex) (, ,  int) float64 {
	if - == 1 {
		.maxRight[] = .entries[].right
		return .maxRight[]
	}
	 :=  + (-)/2
	 := .(*2, , )
	 := .(*2+1, , )
	.maxRight[] = max(, )
	return .maxRight[]
}

func ( *optimizerSpatialIndex) (
	, , ,  float64,
	 *limits.OptimizationWorkGuard,
) ([]int, error) {
	.candidates = .candidates[:0]
	if !.spatialUsable || len(.entries) == 0 {
		return .candidates, .Finish()
	}
	if  := .queryTree(1, 0, len(.entries), , , , , );  != nil {
		return nil, 
	}
	if  := .AddSort(len(.candidates));  != nil {
		return nil, 
	}
	sort.Ints(.candidates)
	return .candidates, .Finish()
}

func ( *optimizerSpatialIndex) (
	, ,  int,
	, , ,  float64,
	 *limits.OptimizationWorkGuard,
) error {
	if  := .Step();  != nil {
		return 
	}
	if  >=  || .maxRight[] <  || .entries[].left >  {
		return nil
	}
	if - == 1 {
		 := .entries[]
		if .left <=  && .right >=  && .top <=  && .bottom >=  {
			.candidates = append(.candidates, .graphIndex)
		}
		return nil
	}

	 :=  + (-)/2
	if  := .(*2, , , , , , , );  != nil {
		return 
	}
	return .(*2+1, , , , , , , )
}

func ( *optimizerSpatialIndex) (
	 *layoutgraph.Graph,
	 *geo.Point,
	 *limits.OptimizationWorkGuard,
) (*layoutgraph.Node, bool, error) {
	if .graph !=  || .nodeCount != len(.Nodes) || !.occupancyUsable ||
		!optimizerIndexFinite(.X) || !optimizerIndexFinite(.Y) {
		return optimizerIsOccupied(, , )
	}
	if  := .Step();  != nil {
		return nil, false, 
	}
	,  := .occupied[*]
	if ! || .generation != .occupancyGeneration {
		return nil, false, .Finish()
	}
	if .graphIndex < 0 || .graphIndex >= len(.Nodes) {
		return nil, false, fmt.Errorf("TALA %s spatial occupancy index is stale", .Location())
	}
	return .Nodes[.graphIndex], true, .Finish()
}

func ( *optimizerSpatialIndex) (
	 *layoutgraph.Node,
	 *geo.Point,
	 []*layoutgraph.Node,
	 *limits.OptimizationWorkGuard,
) (bool, error) {
	if  == nil || .Graph == nil ||  == nil {
		return false, fmt.Errorf("TALA %s overlap check requires a node, graph, and point", .Location())
	}
	if len(.Graph.Nodes) > limits.MaxEngineNodes || len() > limits.MaxEngineNodes {
		return false, fmt.Errorf("TALA %s overlap inputs exceed node limit %d", .Location(), limits.MaxEngineNodes)
	}
	if .graph != .Graph || .nodeCount != len(.Graph.Nodes) || !.spatialUsable ||
		!optimizerIndexFinite(.X) || !optimizerIndexFinite(.Y) ||
		!optimizerIndexFinite(.Width) || !optimizerIndexFinite(.Height) ||
		.Width < 0 || .Height < 0 ||
		!optimizerIndexFinite(.X+.Width) || !optimizerIndexFinite(.Y+.Height) {
		return optimizerDoesOverlap(, , , )
	}

	const  = 500.0
	,  := .query(
		.X-,
		.Y-,
		.X+.Width+,
		.Y+.Height+,
		,
	)
	if  != nil {
		return false, 
	}
	 := .X + .Width
	 := .Y + .Height
	for ,  := range  {
		if  := .Step();  != nil {
			return false, 
		}
		 := .Graph.Nodes[]
		if  ==  {
			continue
		}
		 := false
		for ,  := range  {
			if  := .Step();  != nil {
				return false, 
			}
			if  ==  {
				 = true
				break
			}
		}
		if  || .TopLeft == nil {
			continue
		}
		// The interval query is deliberately only a broad phase. Retain the
		// historical directional spacing calculation and strict comparisons.
		if  := .Add(uint64(len(.Edges)));  != nil {
			return false, 
		}
		 := float64(.DeltaTo(, ))
		if .X < .TopLeft.X+.Width+ && + > .TopLeft.X &&
			.Y < .TopLeft.Y+.Height+ && + > .TopLeft.Y {
			return true, nil
		}
	}
	return false, .Finish()
}