package placement

import (
	
	
	

	
	
	
	
)

// Equidistance centers nodes between aligned neighbors.
//
// A ---------------- B ------ C
// Move B to the midpoint of A and C.
func ( context.Context,  *layoutgraph.Graph) (bool, error) {
	, ,  := layoutgraph.EnsureTransactionWorkGuard(, "EquidistanceTransactions")
	if  != nil {
		return false, 
	}
	if  := layoutgraph.Validate(, "Equidistance", );  != nil {
		return false, 
	}
	,  := snapshotNodePositionsContext(, "Equidistance", .Nodes)
	if  != nil {
		return false, 
	}
	 := false
	defer func() {
		if ! {
			restoreNodePositions()
		}
	}()
	,  := limits.NewWorkGuard(, "EquidistanceReachability", limits.MaxEngineWorkUnits)
	if  != nil {
		return false, 
	}
	 := false
	 := false
	for ,  := range .Nodes {
		,  := equidistanceNodeGuarded(, , , true, )
		if  != nil {
			return false, 
		}
		,  := equidistanceNodeGuarded(, , , false, )
		if  != nil {
			return false, 
		}
		if  {
			 = true
		}
		if  {
			 = true
		}
	}
	// Try multiple times, because moving one may result in different symmetries for others
	if  {
		 := false
		for  := 0;  < 5; ++ {
			for ,  := range .Nodes {
				,  := equidistanceNodeGuarded(, , , true, )
				if  != nil {
					return false, 
				}
				if  {
					 = true
				}
			}
			if ! {
				break
			}
			 = false
		}
	}
	if  {
		 := false
		for  := 0;  < 5; ++ {
			for ,  := range .Nodes {
				,  := equidistanceNodeGuarded(, , , false, )
				if  != nil {
					return false, 
				}
				if  {
					 = true
				}
			}
			if ! {
				break
			}
			 = false
		}
	}

	if  := .Finish();  != nil {
		return false, 
	}
	 = true
	return  || , nil
}

func equidistanceNodeGuarded( context.Context,  *layoutgraph.Node,  *layoutgraph.Graph,  bool,  *limits.WorkGuard) (bool, error) {
	if  := .Err();  != nil {
		return false, 
	}
	if .Hierarchy != nil {
		return false, nil
	}
	if ,  := .NodeToTree[];  {
		return false, nil
	}
	if .IsTreeSentinel() {
		return false, nil
	}
	if .FixedTopLeft != nil {
		return false, nil
	}

	var  *layoutgraph.Node
	var  *layoutgraph.Node

	for ,  := range .Edges {
		 := .Adjacent()
		if .IsDescendantOf() || .IsDescendantOf() {
			return false, nil
		}
		if  {
			// Left
			if .TopLeft.X+.Width < .TopLeft.X {
				if  == nil || .TopLeft.X+.Width > .TopLeft.X+.Width {
					 = 
				}
			}
			// Right
			if .TopLeft.X > .TopLeft.X+.Width {
				if  == nil || .TopLeft.X < .TopLeft.X {
					 = 
				}
			}
		} else {
			// Top
			if .TopLeft.Y+.Height < .TopLeft.Y {
				if  == nil || .TopLeft.Y+.Height > .TopLeft.Y+.Height {
					 = 
				}
			}
			// Bottom
			if .TopLeft.Y > .TopLeft.Y+.Height {
				if  == nil || .TopLeft.Y < .TopLeft.Y {
					 = 
				}
			}
		}
	}

	if  == nil ||  == nil {
		return false, nil
	}

	var  []*layoutgraph.Node
	for ,  := range .Edges {
		 := .Adjacent()
		if  ==  ||  ==  {
			continue
		}
		if  {
			if !.Orientation().IsVertical() {
				continue
			}
		} else {
			if !.Orientation().IsHorizontal() {
				continue
			}
		}
		if .IsDescendantOf() || .IsDescendantOf() {
			continue
		}
		if .IsDescendantOf() || .IsDescendantOf() {
			continue
		}

		,  := .AllReachableNodesContext(false, true, true, map[*layoutgraph.Node]struct{}{
			:            {},
			:  {},
			: {},
		}, )
		if  != nil {
			return false, 
		}

		for ,  := range  {
			if .IsDescendantOf() {
				continue
			}
			if .IsDescendantOf() || .IsDescendantOf() {
				continue
			}
			if .IsDescendantOf() || .IsDescendantOf() {
				continue
			}
		}

		for ,  := range  {
			if !slices.Contains(, ) {
				 = append(, )
			}
		}
	}

	for ,  := range  {
		if .FixedTopLeft != nil {
			 = []*layoutgraph.Node{}
			break
		}
	}

	 := .NearestSharedAncestor()
	 := .NearestSharedAncestor()

	/*

			We want to be able to move the whole container
		 ┌────┐           ┌─────────┐        ┌──────┐
		 │    │           │         │        │      │
		 │    │           │  ┌───┐  │        │      │
		 │    │           │  └───┘  │        │      │
		 └────┘           │         │        │      │
		                  └─────────┘        └──────┘

	*/
	for .OwningContainer() != nil && !.IsDescendantOf(.OwningContainer()) && !.IsDescendantOf(.OwningContainer()) {
		 = .OwningContainer()
	}

	/*

			If b is the nearest back in !isHorizontal, b's container is relevant and should be used.

						┌─────┐
						│     │
						│  a  │
						└─────┘



			┌────────────────┐
			│                │
			│                │
			│                │
			│     ┌────┐     │
			│     │ b  │     │
			│     │    │     │
			│     └────┘     │
			│                │
			│                │
			│                │
			└────────────────┘

			But if it's like this, we don't care


		                      ┌──────┐
		                      │      │
		┌────────────────┐    │  a   │
		│                │    │      │
		│                │    └──────┘
		│                │
		│     ┌────┐     │
		│     │ b  │     │
		│     │    │     │
		│     └────┘     │
		│                │
		│                │
		│                │
		└────────────────┘
	*/
	for .OwningContainer() !=  {
		 := .OwningContainer()
		if  {
			if .TopLeft.X+.Width < .TopLeft.X {
				 = .OwningContainer()
			} else {
				break
			}
		} else {
			if .TopLeft.Y+.Height < .TopLeft.Y {
				 = .OwningContainer()
			} else {
				break
			}
		}
	}
	for .OwningContainer() !=  {
		 := .OwningContainer()
		if  {
			if .TopLeft.X > .TopLeft.X+.Width {
				 = .OwningContainer()
			} else {
				break
			}
		} else {
			if .TopLeft.Y > .TopLeft.Y+.Height {
				 = .OwningContainer()
			} else {
				break
			}
		}
	}

	,  := placementcost.EdgeLength(, , placementcost.EdgeLengthOptions{EdgeAbductions: nil, IncludeNodeSizes: true, EnforceMinimumGap: false, PenalizeDirection: true})
	if  != nil {
		return false, 
	}

	 := 0.
	 := 0.
	if  {
		 := math.Round((.TopLeft.X+.Width+.TopLeft.X)/2. - .Width/2.)
		 =  - .TopLeft.X
	} else {
		 := math.Round((.TopLeft.Y+.Height+.TopLeft.Y)/2. - .Height/2.)
		 =  - .TopLeft.Y
	}

	,  := .NewRequestTransaction(, layoutgraph.TransactionOptions{AffectContainers: true})
	if  != nil {
		return false, 
	}

	 := false
	var  float64
	var  float64

	// First try moving just the node by itself
	.AddOp(func() error {
		.MoveWithChildren(, )
		return nil
	})
	if  := .Commit();  != nil {
		if !layoutgraph.IsCandidateRejection() {
			return false, 
		}
		 = true
	}
	if ! {
		,  := placementcost.EdgeLength(, , placementcost.EdgeLengthOptions{EdgeAbductions: nil, IncludeNodeSizes: true, EnforceMinimumGap: false, PenalizeDirection: true})
		if  != nil {
			.Rollback()
			return false, 
		}
		if geo.PrecisionCompare(, , geo.PRECISION) <= 0 {
			 = 
		}
		.Rollback()
	}

	.Clear()

	// Then try moving with connected
	if len() > 0 {
		 = false
		.AddOp(func() error {
			for ,  := range append(, ) {
				.MoveWithChildren(, )
			}
			return nil
		})
		if  := .Commit();  != nil {
			if !layoutgraph.IsCandidateRejection() {
				return false, 
			}
			 = true
		}
		if ! {
			,  := placementcost.EdgeLength(, , placementcost.EdgeLengthOptions{EdgeAbductions: nil, IncludeNodeSizes: true, EnforceMinimumGap: false, PenalizeDirection: true})
			if  != nil {
				.Rollback()
				return false, 
			}
			if geo.PrecisionCompare(, , geo.PRECISION) <= 0 {
				 = 
			}
			.Rollback()
		}
	}

	.Clear()

	// Then use the best global edge length and move it there
	if  == 0 &&  == 0 {
		return false, nil
	} else {
		.AddOp(func() error {
			if  == 0 &&  > 0 {
				for ,  := range append(, ) {
					.MoveWithChildren(, )
				}
			} else if  > 0 &&  == 0 {
				.MoveWithChildren(, )
			} else if geo.PrecisionCompare(, , geo.PRECISION) < 0 {
				.MoveWithChildren(, )
			} else {
				for ,  := range append(, ) {
					.MoveWithChildren(, )
				}
			}

			return nil
		})

		if  := .Commit();  != nil {
			return false, 
		}
	}
	return true, nil
}