package packing

import (
	
	

	
	
	
)

type routedContainerSide uint8

type routedContainerBoxDecision uint8

const (
	routedContainerDeferToSideConstraints routedContainerBoxDecision = iota
	routedContainerKeepOriginalBox
	routedContainerUseProposedBox
)

const (
	routedContainerTop routedContainerSide = 1 << iota
	routedContainerLeft
	routedContainerBottom
	routedContainerRight

	// D2 renders an edge with this corner radius when no explicit edge style
	// overrides it. TALA retains nil style provenance, so the safety proof must
	// account for the renderer default instead of treating nil as a straight path.
	routedContainerDefaultEdgeBorderRadius  = 10.
	routedContainerMaxBorderRadiusTextBytes = 64
)

// binPackCanUseRoutedContainerBox reports whether the box currently proposed
// on root preserves every affected routed segment and supported root route
// attachment from original. It is intentionally narrow: root self-loops,
// root-to-descendant routes, and curved routes keep the original box. A direct
// root route is required to supply the side-attachment contract for an
// admissible shrink; descendant-only routed containers defer to BinPack's
// ordinary side constraints. The new box must remain inside the old one, and
// every side touched by a root endpoint must remain geometrically identical.
// Preserving the complete side also keeps renderer-only rounded corners from
// moving onto a fixed endpoint.
func binPackCanUseRoutedContainerBox(
	 *layoutgraph.Graph,
	 *layoutgraph.Node,
	 *geo.Box,
	 []*layoutgraph.Edge,
	 *limits.WorkGuard,
) (routedContainerBoxDecision, error) {
	if  := .Step();  != nil {
		return routedContainerDeferToSideConstraints, 
	}
	if  == nil ||  == nil || .Graph !=  {
		return routedContainerKeepOriginalBox, nil
	}
	if len() == 0 {
		// With no root-incident route, ordinary side constraints determine which
		// coordinates the wrapped box may change.
		return routedContainerDeferToSideConstraints, .Finish()
	}
	if .TopLeft == nil || .Shape == nil ||  == nil || .TopLeft == nil {
		return routedContainerKeepOriginalBox, nil
	}

	,  := .AllDescendantNodesWithWorkGuard(, true, )
	if  != nil {
		return routedContainerDeferToSideConstraints, 
	}
	 := make(map[*layoutgraph.Node]struct{}, len())
	for ,  := range  {
		if  := .Step();  != nil {
			return routedContainerDeferToSideConstraints, 
		}
		if  == nil {
			return routedContainerKeepOriginalBox, nil
		}
		[] = struct{}{}
	}

	 := make([]*layoutgraph.Edge, 0, len())
	 := make(map[*layoutgraph.Edge]struct{}, len())
	for ,  := range .Edges {
		if  := .Step();  != nil {
			return routedContainerDeferToSideConstraints, 
		}
		if  == nil || .From == nil || .To == nil {
			return routedContainerKeepOriginalBox, nil
		}
		,  := [.From]
		,  := [.To]
		if .From !=  && .To !=  && ! && ! {
			continue
		}
		if ,  := [];  {
			return routedContainerKeepOriginalBox, nil
		}
		[] = struct{}{}
		 = append(, )
	}
	 := make(map[*layoutgraph.Edge]struct{}, len())
	for ,  := range  {
		if  := .Step();  != nil {
			return routedContainerKeepOriginalBox, 
		}
		if  == nil || (.From !=  && .To != ) {
			return routedContainerKeepOriginalBox, nil
		}
		if ,  := [];  {
			return routedContainerKeepOriginalBox, nil
		}
		if ,  := []; ! {
			return routedContainerKeepOriginalBox, nil
		}
		[] = struct{}{}
	}
	if !.IsContainer() || !.CanContain() || !.Shape.IsRectangular() {
		return routedContainerKeepOriginalBox, nil
	}
	if ,  := .ModifierElementAdjustments();  != 0 ||  != 0 {
		return routedContainerKeepOriginalBox, nil
	}
	if !routedContainerBoxIsFinite(&.Box) || !routedContainerBoxIsFinite() {
		return routedContainerKeepOriginalBox, nil
	}
	if !routedContainerBoxCovers(, &.Box) {
		return routedContainerKeepOriginalBox, nil
	}
	if .FixedTopLeft != nil &&
		(.TopLeft.X != .TopLeft.X || .TopLeft.Y != .TopLeft.Y) {
		return routedContainerKeepOriginalBox, nil
	}

	 := false
	 := make(map[*layoutgraph.Edge]struct{}, len(.Edges))
	for ,  := range .Edges {
		if  := .Step();  != nil {
			return routedContainerKeepOriginalBox, 
		}
		if  == nil || .From == nil || .To == nil || .From == .To || len(.Points) < 2 {
			return routedContainerKeepOriginalBox, nil
		}
		if ,  := []; ! {
			return routedContainerKeepOriginalBox, nil
		}
		if ,  := [];  {
			return routedContainerKeepOriginalBox, nil
		}
		[] = struct{}{}
		 := false
		if .From ==  {
			 = true
			 = true
			if  := .Step();  != nil {
				return routedContainerKeepOriginalBox, 
			}
			if !routedContainerEndpointKeepsSides(, &.Box, .Points[0]) {
				return routedContainerKeepOriginalBox, nil
			}
		}
		if .To ==  {
			 = true
			 = true
			if  := .Step();  != nil {
				return routedContainerKeepOriginalBox, 
			}
			if !routedContainerEndpointKeepsSides(, &.Box, .Points[len(.Points)-1]) {
				return routedContainerKeepOriginalBox, nil
			}
		}
		if ! {
			return routedContainerKeepOriginalBox, nil
		}
		 := .From
		if  ==  {
			 = .To
		}
		,  := binPackIsDescendantOf(, , )
		if  != nil {
			return routedContainerKeepOriginalBox, 
		}
		if  {
			// A route to a descendant may leave and re-enter the proposed
			// box even while its root endpoint remains attached.
			return routedContainerKeepOriginalBox, nil
		}
	}
	if len() != len() || ! {
		return routedContainerKeepOriginalBox, nil
	}
	,  := routedContainerRoutesStayInsideShrink(
		, , &.Box, ,
	)
	if  != nil {
		return routedContainerKeepOriginalBox, 
	}
	if  := .Finish();  != nil {
		return routedContainerKeepOriginalBox, 
	}
	if  {
		return routedContainerUseProposedBox, nil
	}
	return routedContainerKeepOriginalBox, nil
}

// routedContainerRoutesStayInsideShrink proves that shrinking a routed
// container does not discard any part of an affected route that was inside the
// original outer box. For each straight route segment, its intersection with
// the original rectangle is another segment. Both clipped endpoints must be in
// the proposed rectangle; convexity then contains the complete clipped segment.
// This is deliberately an engine outer-box invariant: layoutgraph does not
// carry arbitrary D2 node BorderRadius, so it does not claim containment by a
// renderer-specific rounded node outline. Direct root attachments separately
// preserve their complete endpoint-bearing side.
func routedContainerRoutesStayInsideShrink(
	 []*layoutgraph.Edge,
	,  *geo.Box,
	 *limits.WorkGuard,
) (bool, error) {
	for ,  := range  {
		if  := .Step();  != nil {
			return false, 
		}
		if  == nil || .From == nil || .To == nil || len(.Points) < 2 || .IsCurve {
			return false, nil
		}

		for ,  := range .Points {
			if  := .Step();  != nil {
				return false, 
			}
			if !routedContainerPointIsFinite() {
				return false, nil
			}
		}
		,  := routedContainerRoundedCornersStayInsideShrink(
			, , , ,
		)
		if  != nil {
			return false, 
		}
		if ! {
			return false, nil
		}

		for  := 0;  < len(.Points)-1; ++ {
			if  := .Step();  != nil {
				return false, 
			}
			,  := routedContainerSegmentStaysInsideShrink(
				, , .Points[], .Points[+1], ,
			)
			if  != nil {
				return false, 
			}
			if ! {
				return false, nil
			}
		}
	}
	return true, .Finish()
}

// routedContainerRoundedCornersStayInsideShrink accounts for the renderer's
// rounded polyline corners. The ordinary rounded corner is a cubic Bezier
// contained by the entry/corner/exit control hull. A hull wholly inside the
// proposed box is preserved; a hull wholly outside one side of the original box
// never affected the container. Ambiguous hulls and the renderer's combined
// short-segment curve fail closed.
func routedContainerRoundedCornersStayInsideShrink(
	 *layoutgraph.Edge,
	,  *geo.Box,
	 *limits.WorkGuard,
) (bool, error) {
	 := routedContainerDefaultEdgeBorderRadius
	if .Style.BorderRadius != nil {
		if len(.Style.BorderRadius.Value) > routedContainerMaxBorderRadiusTextBytes {
			return false, nil
		}
		var  error
		,  = strconv.ParseFloat(.Style.BorderRadius.Value, 64)
		if  != nil {
			return false, nil
		}
	}
	if !routedContainerCoordinateIsFinite() ||  < 0 {
		return false, nil
	}

	for  := 1;  < len(.Points)-1; ++ {
		if  := .Step();  != nil {
			return false, 
		}
		, ,  := .Points[-1], .Points[], .Points[+1]
		,  := .X-.X, .Y-.Y
		,  := .X-.X, .Y-.Y
		 := math.Hypot(, )
		 := math.Hypot(, )
		if !routedContainerCoordinateIsFinite() ||  <= 0 ||
			!routedContainerCoordinateIsFinite() ||  <= 0 {
			return false, nil
		}
		if  == 0 {
			continue
		}
		if  <  || /2 <  {
			// Short corners use a different effective radius and may combine
			// controls across vertices. This proof only admits the normal branch.
			return false, nil
		}

		 := geo.Point{
			X: .X - /*,
			Y: .Y - /*,
		}
		 := geo.Point{
			X: .X + /*,
			Y: .Y + /*,
		}
		if !routedContainerPointIsFinite(&) || !routedContainerPointIsFinite(&) ||
			!routedContainerControlHullIsPreserved(, , &, , &) {
			return false, nil
		}
	}
	return true, nil
}

func routedContainerControlHullIsPreserved(,  *geo.Box,  ...*geo.Point) bool {
	 := true
	, , ,  := true, true, true, true
	,  := .TopLeft.X, .TopLeft.Y
	 :=  + .Width
	 :=  + .Height
	for ,  := range  {
		if !routedContainerPointIsFinite() {
			return false
		}
		 =  && .Contains()
		 =  && .X < 
		 =  && .Y < 
		 =  && .X > 
		 =  && .Y > 
	}
	return  ||  ||  ||  || 
}

// routedContainerSegmentStaysInsideShrink clips a straight segment to the
// closed original rectangle with Liang-Barsky. An empty intersection is
// unaffected; otherwise both ends of the clipped segment must be contained by
// the closed proposed rectangle.
func routedContainerSegmentStaysInsideShrink(
	,  *geo.Box,
	,  *geo.Point,
	 *limits.WorkGuard,
) (bool, error) {
	if !routedContainerBoxIsFinite() || !routedContainerBoxIsFinite() ||
		!routedContainerPointIsFinite() || !routedContainerPointIsFinite() {
		return false, nil
	}
	if .X == .X && .Y == .Y {
		return false, nil
	}
	, , , ,  := routedContainerSegmentBoxInterval(
		, , , ,
	)
	if  != nil || ! {
		return false, 
	}
	if ! {
		return true, nil
	}
	, , , ,  := routedContainerSegmentBoxInterval(
		, , , ,
	)
	if  != nil || ! {
		return false, 
	}
	return  &&  <=  &&  >= , nil
}

func routedContainerSegmentBoxInterval(
	 *geo.Box,
	,  *geo.Point,
	 *limits.WorkGuard,
) (,  float64, ,  bool,  error) {
	,  := .X-.X, .Y-.Y
	if !routedContainerCoordinateIsFinite() || !routedContainerCoordinateIsFinite() {
		return 0, 0, false, false, nil
	}

	,  := .TopLeft.X, .TopLeft.Y
	,  := +.Width, +.Height
	 := [][2]float64{
		{-, .X - },
		{,  - .X},
		{-, .Y - },
		{,  - .Y},
	}
	,  := 0., 1.
	for ,  := range  {
		if  := .Step();  != nil {
			return 0, 0, false, false, 
		}
		,  := [0], [1]
		if  == 0 {
			if  < 0 {
				return 0, 0, false, true, nil
			}
			continue
		}
		 :=  / 
		if !routedContainerCoordinateIsFinite() {
			return 0, 0, false, false, nil
		}
		if  < 0 {
			if  >  {
				return 0, 0, false, true, nil
			}
			if  >  {
				 = 
			}
		} else {
			if  <  {
				return 0, 0, false, true, nil
			}
			if  <  {
				 = 
			}
		}
	}
	return , , true, true, nil
}

func routedContainerEndpointKeepsSides(,  *geo.Box,  *geo.Point) bool {
	 := routedContainerPointSides(, )
	if  == 0 {
		return false
	}
	,  := .TopLeft.X, .TopLeft.Y
	 :=  + .Width
	 :=  + .Height
	,  := .TopLeft.X, .TopLeft.Y
	 :=  + .Width
	 :=  + .Height

	if &routedContainerTop != 0 &&
		( !=  ||  !=  ||  != ) {
		return false
	}
	if &routedContainerLeft != 0 &&
		( !=  ||  !=  ||  != ) {
		return false
	}
	if &routedContainerBottom != 0 &&
		( !=  ||  !=  ||  != ) {
		return false
	}
	if &routedContainerRight != 0 &&
		( !=  ||  !=  ||  != ) {
		return false
	}
	return true
}

func routedContainerPointSides( *geo.Box,  *geo.Point) routedContainerSide {
	if !routedContainerBoxIsFinite() || !routedContainerPointIsFinite() {
		return 0
	}
	,  := .TopLeft.X, .TopLeft.Y
	,  := +.Width, +.Height
	var  routedContainerSide
	if .Y ==  && .X >=  && .X <=  {
		 |= routedContainerTop
	}
	if .X ==  && .Y >=  && .Y <=  {
		 |= routedContainerLeft
	}
	if .Y ==  && .X >=  && .X <=  {
		 |= routedContainerBottom
	}
	if .X ==  && .Y >=  && .Y <=  {
		 |= routedContainerRight
	}
	return 
}

func routedContainerPointIsFinite( *geo.Point) bool {
	return  != nil && routedContainerCoordinateIsFinite(.X) && routedContainerCoordinateIsFinite(.Y)
}

func routedContainerBoxCovers(,  *geo.Box) bool {
	 := .TopLeft.X + .Width
	 := .TopLeft.Y + .Height
	 := .TopLeft.X + .Width
	 := .TopLeft.Y + .Height
	return .TopLeft.X >= .TopLeft.X && .TopLeft.Y >= .TopLeft.Y &&
		 <=  &&  <= 
}

func routedContainerBoxIsFinite( *geo.Box) bool {
	return  != nil && .TopLeft != nil && .Width > 0 && .Height > 0 &&
		routedContainerCoordinateIsFinite(.TopLeft.X) && routedContainerCoordinateIsFinite(.TopLeft.Y) &&
		routedContainerCoordinateIsFinite(.Width) && routedContainerCoordinateIsFinite(.Height) &&
		routedContainerCoordinateIsFinite(.TopLeft.X+.Width) &&
		routedContainerCoordinateIsFinite(.TopLeft.Y+.Height)
}

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