package placement

import (
	
	

	
	
	
	
)

type containerOrientationDisabled struct{}

// A container footprint can change the later placement of its siblings. Until
// those later operations validate self-loop envelopes, leave loop-bearing
// layouts on their existing placement path. This scan happens once per attempt.
func orientationContext( context.Context,  *layoutgraph.Graph) (context.Context, error) {
	,  := limits.NewWorkGuard(, "ContainerOrientationPreflight", limits.MaxEngineWorkUnits)
	if  != nil {
		return , 
	}
	for ,  := range .Edges {
		if  := .Step();  != nil {
			return , 
		}
		if .IsLoop() {
			return context.WithValue(, containerOrientationDisabled{}, true), nil
		}
	}
	return , .Finish()
}

// orientSourceInterior aligns the internal flow of a small source container
// with its surrounding flow, before the parent is fitted around the result.
// The optional move is local: it does not add another complete layout attempt.
func orientSourceInterior( context.Context,  *layoutgraph.Graph,  *layoutgraph.Node,  []geo.Box) error {
	if  := .Err();  != nil {
		return 
	}
	if .Value(containerOrientationDisabled{}) == true ||  == nil || len(.Nodes) < 3 || len(.Nodes) > 16 || len() != 0 || .FixedTopLeft != nil || .Cluster != nil || .Sequence != nil || .ForceHierarchy || .Graph.Direction() != geo.NONE {
		return nil
	}
	 := .Direction(.OwningContainer())
	if !.IsVertical() || len(.Edges) < 3 {
		return nil
	}
	,  := limits.NewWorkGuard(, "ContainerOrientation", limits.MaxEngineWorkUnits)
	if  != nil {
		return 
	}
	 := make(map[*layoutgraph.Node]struct{})
	for ,  := range .Edges {
		if  := .Step();  != nil {
			return 
		}
		, ,  := .DirectedEndpoints()
		if ! ||  !=  ||  ==  {
			return nil
		}
		[] = struct{}{}
	}
	if len() < 2 {
		return nil
	}
	 := make(map[*layoutgraph.Node]bool, len(.Nodes))
	for ,  := range .Nodes {
		if .IsContainer() || .Hierarchy != nil || .FixedTopLeft != nil || .Sequence != nil || .IsTreeSentinel() || .IsSequenceVessel() || .HerdAssignment != nil {
			return nil
		}
		// Self-loop envelopes do not turn with the boxes. Keep their established
		// placement until orientation can preserve that extra route clearance.
		for ,  := range .LoopOffsets {
			if  > 0 {
				return nil
			}
		}
		[] = true
	}
	for ,  := range .Nodes {
		for  := range .Nears {
			if ![] {
				return nil
			}
		}
	}
	,  := interiorFlow(, , )
	if  != nil {
		return 
	}
	// A quarter-turn needs a clear transverse flow to improve. Disconnected
	// contents, balanced cycles and already vertical compositions are left alone.
	if .across <= .along+1e-9 || math.Abs(.x) <= 1e-9 {
		return nil
	}
	 := 1.0
	if (.x < 0) != ( == geo.Top) {
		 = -1
	}

	,  := orientationFootprintSize(, )
	,  := .NewRequestTransaction(, layoutgraph.TransactionOptions{IgnoreContainerEscape: true})
	if  != nil {
		return 
	}
	.AddOp(func() error {
		 := make(map[*layoutgraph.Node]geo.Point, len(.Nodes))
		for ,  := range .Nodes {
			[] = *.Center()
		}
		for ,  := range .Nodes {
			if  := .Step();  != nil {
				return 
			}
			if  := .Clusters[];  != nil {
				.Arrangement = .Arrangement.Flip()
				.DesiredArrangement = .Arrangement
				// Preserve the group's logical member order, but recalculate spacing for
				// its new axis, including icon and outside-label room.
				.Padding = grouping.PaddingBetween(, true)
				.Resize()
			}
			 := []
			.TopLeft = geo.NewPoint(math.Round(-*.Y-.Width/2), math.Round(*.X-.Height/2))
		}
		.SyncNestedGeometry()
		 := .CellSize
		.CellSize = 1
		defer func() { .CellSize =  }()
		for ,  := range []layoutAxis{horizontalAxis, verticalAxis} {
			if  := compaction(, , compactionOptions{axis: , includeSizes: true, factor: 1, transition: true});  != nil {
				return 
			}
		}
		.SyncNestedGeometry()
		,  := .BoundingBox()
		,  := orientationFootprintSize(, )
		if .X-.X > limits.MaxGraphSize || .Y-.Y > limits.MaxGraphSize || !( >= 0 &&  >= 0 &&  <= limits.MaxGraphSize &&  <= limits.MaxGraphSize) {
			return layoutgraph.ErrInvalidCandidate
		}
		// Upright boxes can expand substantially during a quarter-turn. Allow
		// at most one additional original footprint, including the containing
		// shape, label room, and authored minimum dimensions.
		 := * > 2**
		if  {
			return layoutgraph.ErrNonImprovingCandidate
		}
		,  := interiorFlow(, , )
		if  != nil {
			return 
		}
		 := 1.0
		if  == geo.Top {
			 = -1
		}
		if *.y <= *.y+1e-9 {
			return layoutgraph.ErrNonImprovingCandidate
		}
		return .Finish()
	})
	// Commit validates node clearances and restores positions, dimensions and
	// cluster metadata on rejection or error. Root fitting happens after success.
	if  := .Commit();  != nil {
		if layoutgraph.IsCandidateRejection() {
			return nil
		}
		return 
	}
	return nil
}

// interiorFlow gives each distinct directed adjacency one vote, normalized by
// its Manhattan length so a single long edge cannot dominate the orientation.
type containerFlow struct{ x, y, across, along float64 }

func interiorFlow( *layoutgraph.Graph,  map[*layoutgraph.Node]bool,  *limits.WorkGuard) ( containerFlow,  error) {
	 := make(map[[2]*layoutgraph.Node]bool)
	for ,  := range .Edges {
		if  := .Step();  != nil {
			return containerFlow{}, 
		}
		, ,  := .DirectedEndpoints()
		if ! ||  ==  || ![] || ![] {
			continue
		}
		 := [2]*layoutgraph.Node{, }
		if [] {
			continue
		}
		[] = true
		,  := .Center(), .Center()
		,  := .X-.X, .Y-.Y
		 := math.Abs() + math.Abs()
		if  == 0 {
			continue
		}
		.x +=  / 
		.across += math.Abs() / 
		.y +=  / 
		.along += math.Abs() / 
	}
	return , .Finish()
}

// Use the actual fitting path, including label room, shape geometry and authored
// minimum dimensions. Its only mutations are Width/Height; restore both before
// returning so optional admission does not resize the parent early.
func orientationFitsContainer( *layoutgraph.Graph,  *layoutgraph.Node) bool {
	,  := orientationFootprintSize(, )
	return  >= 0 &&  >= 0 &&  <= limits.MaxGraphSize &&  <= limits.MaxGraphSize
}

// Fitting mutates only the containing node's dimensions. Restore those values
// so observing the original or optional footprint never resizes the parent early.
func orientationFootprintSize( *layoutgraph.Graph,  *layoutgraph.Node) (,  float64) {
	,  := .Width, .Height
	defer func() { .Width, .Height = ,  }()
	.FitToGraph(, .ContainerPadding(, false))
	return .Width, .Height
}