package placementcost

import (
	
	

	
	
)

type edgeLengthStatic struct {
	direction       geo.Orientation
	directionFactor float64
}

// NodeEdgeLengthScorer reuses incident-edge topology during one candidate sweep.
// Geometry is read from live nodes on every Score. The caller must keep edges,
// abductions, container/cluster membership, labels and directions unchanged until
// Close. In particular, a scorer must not survive a topology-changing swap.
// It is local mutable scratch and must not be shared between goroutines.
type NodeEdgeLengthScorer struct {
	node       *layoutgraph.Node
	options    EdgeLengthOptions
	scratch    *edgeScratch
	setup      edgeLengthStatic
	prepChecks int
}

// NewNodeEdgeLengthScorer starts an unprepared sweep. Preparation is lazy so
// cancellation and the caller's work charge occur at the original score point.
// The returned value must not be copied after its first Score; call Close when
// the sweep ends to release references retained by its pooled scratch.
func ( *layoutgraph.Node,  EdgeLengthOptions) NodeEdgeLengthScorer {
	return NodeEdgeLengthScorer{node: , options: }
}

// Score has the same value and cancellation checkpoints as NodeEdgeLength while
// the scorer's topology remains fixed. The caller still charges the original
// scoring work budget for each candidate; reusing setup does not bypass limits.
func ( *NodeEdgeLengthScorer) ( context.Context) (float64, error) {
	if  := .Err();  != nil {
		return 0, fmt.Errorf("EdgeLength: %w", )
	}
	if .node == nil {
		return 0, fmt.Errorf("EdgeLength: scorer is closed")
	}
	if .scratch == nil {
		 := scratchPool.Get()
		// Publish ownership before preparation so a caller's deferred Close also
		// releases scratch if a malformed graph or context panics during setup.
		.scratch = 
		 := edgeLengthPreparationContext{Context: }
		,  := prepareNodeEdgeLength(&, .node, .options, )
		if  != nil {
			putEdgeScratch()
			.scratch = nil
			return 0, 
		}
		.scratch, .setup, .prepChecks = , , .count
	} else {
		// Preparation only inspects topology. Replay its exact polls without
		// repeating endpoint matching and label-count construction. This keeps
		// cancellation observability equal even for a context counting Err calls.
		for range .prepChecks {
			if  := checkScoringCancellation();  != nil {
				return 0, 
			}
		}
	}
	return evaluateNodeEdgeLength(, .node, .options, .scratch, .setup)
}

// Close releases scratch and makes the scorer unusable. It is safe to call more
// than once, including after an unsuccessful Score.
func ( *NodeEdgeLengthScorer) () {
	if .scratch != nil {
		putEdgeScratch(.scratch)
	}
	* = NodeEdgeLengthScorer{}
}

type edgeLengthPreparationContext struct {
	context.Context
	count int
}

func ( *edgeLengthPreparationContext) () error {
	.count++
	return .Context.Err()
}