package engine

import (
	
	

	
	
	
	
	
	
	
)

// PreserveCompoundRoutes refines an already-selected compound drawing. Keep
// placement selection independent of this routing cleanup: otherwise a cheaper
// reroute can promote an unrelated, much wider outer arrangement.
func ( context.Context, ,  *layoutgraph.Graph) (*layoutgraph.Graph, error) {
	,  := layoutgraph.Clone(, )
	if  != nil {
		return nil, 
	}
	// The selected proposal already has complete external routes. Preserve their
	// curve state too; Clone resets it for ordinary full-layout attempts.
	for ,  := range .Edges {
		.IsCurve = .Edges[].IsCurve
	}
	if  := preserveCompoundInteriors(, , );  != nil {
		return nil, 
	}
	return , nil
}

// A compound trial moves each outer block rigidly. Its enclosed routes and
// labels belong to that block too: redrawing them can break a shared fan even
// though none of its endpoints or obstacles moved relative to one another.
func preserveCompoundInteriors( context.Context, ,  *layoutgraph.Graph) error {
	,  := limits.NewWorkGuard(, "CompoundRoutes", limits.MaxEngineWorkUnits)
	if  != nil {
		return 
	}
	// Clone resets placement bookkeeping for a new full layout. This trial
	// instead retains the authored label and icon constraints of placed nodes.
	for ,  := range .Nodes {
		 := .Nodes[]
		if .Label != nil && .Label.PositionFixed() {
			 := *.Label
			.Label = &
		}
		if .Icon != nil && .Icon.PositionFixed() {
			 := *.Icon
			.Icon = &
		}
	}
	// Clone and PlaceCompound preserve edge order. IDs may be unset in graphs
	// constructed directly by engine callers, so they cannot identify this pair.
	for ,  := range .Edges {
		if  := .Err();  != nil {
			return 
		}
		 := .Edges[]
		if .Label != nil && .Label.PositionFixed() {
			 := *.Label
			.Label = &
			.LabelPercentage = .LabelPercentage
		}
		,  := compoundEnclosedRoute(, )
		if  != nil {
			return 
		}
		if  {
			,  := compoundRoot(.From), compoundRoot(.From)
			,  := .TopLeft.X-.TopLeft.X, .TopLeft.Y-.TopLeft.Y
			.Points = make([]*geo.Point, len(.Points))
			for ,  := range .Points {
				if  := .Step();  != nil {
					return 
				}
				.Points[] = geo.NewPoint(.X+, .Y+)
			}
			.IsCurve = .IsCurve
			.LabelPercentage = .LabelPercentage
			if .Label != nil {
				 := *.Label
				.Label = &
			}
			continue
		}
	}
	.ResetPlacementCosts()
	// The selected external routes can use a previously unused side of an interior node.
	// Reconsider automatic labels and icons against those new attachments.
	if  := labeling.Place(, );  != nil {
		return 
	}
	placement.Normalize()
	return .Finish()
}

func compoundRoot( *layoutgraph.Node) *layoutgraph.Node {
	for .Container != nil {
		 = .Container
	}
	return 
}

func compoundEnclosedRoute( *layoutgraph.Edge,  *limits.WorkGuard) (bool, error) {
	 := compoundRoot(.From)
	if !.IsContainer() ||  != compoundRoot(.To) || len(.Points) < 2 {
		return false, nil
	}
	 := func( *geo.Point, ,  float64) bool {
		return  != nil && !math.IsNaN(.X) && !math.IsNaN(.Y) &&
			.X >= .TopLeft.X && .Y >= .TopLeft.Y &&
			.X+ <= .TopLeft.X+.Width && .Y+ <= .TopLeft.Y+.Height
	}
	for ,  := range .Points {
		if  := .Step();  != nil {
			return false, 
		}
		if !(, 0, 0) {
			return false, nil
		}
	}
	// Conservatively reuse only endpoints on box borders. Shape-specific ports
	// recessed inside an oval or other outline can be rebuilt by the router.
	 := func( *layoutgraph.Node,  *geo.Point) bool {
		return .ContainsPointOnBox() &&
			(.X == .TopLeft.X || .X == .TopLeft.X+.Width ||
				.Y == .TopLeft.Y || .Y == .TopLeft.Y+.Height)
	}
	if !(.From, .Points[0]) || !(.To, .Points[len(.Points)-1]) {
		return false, nil
	}
	if .Label != nil {
		if .Label.Position == label.Unset {
			return false, nil
		}
		if  := .Add(int64(len(.Points)));  != nil {
			return false, 
		}
		if !(.LabelTopLeft(.Label.Position, .Label.Width, .Label.Height), .Label.Width, .Label.Height) {
			return false, nil
		}
	}
	for ,  := range []*layoutgraph.Label{.SourceArrowheadLabel, .TargetArrowheadLabel} {
		if  == nil {
			continue
		}
		if  := .Add(int64(len(.Points)));  != nil {
			return false, 
		}
		 := labelgeom.ArrowheadTopLeft(.Points,  == 1, string(.SourceArrowhead), string(.TargetArrowhead), .Width, .Height)
		if !(, .Width, .Height) {
			return false, nil
		}
	}
	return true, nil
}