package grouping

import (
	
	
	

	
	
	
	
)

type joinNodePositionSnapshot struct {
	node    *layoutgraph.Node
	pointer *geo.Point
	value   geo.Point
}

// joinPositionJournal keeps capture order because separate nodes may share a
// TopLeft pointer and first reach the journal after different movements.
type joinPositionJournal struct {
	captured  map[*layoutgraph.Node]struct{}
	snapshots []joinNodePositionSnapshot
}

func ( *joinPositionJournal) ( *layoutgraph.Node) bool {
	if  == nil {
		return false
	}
	if .captured == nil {
		.captured = make(map[*layoutgraph.Node]struct{})
	}
	if ,  := .captured[];  {
		return false
	}
	.captured[] = struct{}{}
	 := joinNodePositionSnapshot{node: , pointer: .TopLeft}
	if .TopLeft != nil {
		.value = *.TopLeft
	}
	.snapshots = append(.snapshots, )
	return true
}

func ( *joinPositionJournal) (
	 *layoutgraph.Node,
	,  float64,
	 *limits.WorkGuard,
) error {
	if  == 0 &&  == 0 {
		return nil
	}
	 := .capture()
	if .Graph == nil {
		if  := .Step();  != nil {
			return 
		}
		// Preserve the legacy malformed-node panic after journaling the root.
		.MoveWithChildren(, )
		return .Finish()
	}
	 := .Graph
	 := .IsContainer() && len(.Containers[]) > 0
	if .IsClusterVessel() {
		 =  || .Clusters[] != nil && len(.Clusters[].Nodes) > 0
	}
	 =  || .Sequences[] != nil && len(.Sequences[].Nodes) > 0
	var  []*layoutgraph.Node
	if  {
		var  error
		,  = .AllDescendantNodesWithWorkGuard(, true, )
		if  != nil {
			return 
		}
	}
	if  {
		for ,  := range  {
			.capture()
		}
	}
	// Reject the complete translation before changing any shared point. The
	// guarded descendant walk above separately accounts hierarchy discovery.
	if  := .Add(int64(1 + len()));  != nil {
		return 
	}
	.Translate(, )
	for ,  := range  {
		.Translate(, )
	}
	return nil
}

func ( *joinPositionJournal) () {
	// Reverse capture order restores shared pointers through every intermediate
	// value before returning them to their original value.
	for  := len(.snapshots) - 1;  >= 0; -- {
		 := .snapshots[]
		.node.TopLeft = .pointer
		if .pointer != nil {
			*.pointer = .value
		}
	}
}

func joinMedian( []*geo.Point,  *limits.WorkGuard) (*geo.Point, error) {
	if  := .Add(2 * int64(len()));  != nil {
		return nil, 
	}
	for  := 1;  < len();  *= 2 {
		if  := .Add(2 * int64(len()));  != nil {
			return nil, 
		}
		if  > len()/2 {
			break
		}
	}
	return geo.Points().GetMedian(), nil
}

func joinContainerFixedOrigin(
	 *layoutgraph.Graph,
	 *layoutgraph.Node,
	 *limits.WorkGuard,
) (*geo.Point, error) {
	for ,  := range .Nodes {
		if  := .Step();  != nil {
			return nil, 
		}
		if .OwningContainer() !=  {
			continue
		}
		if  := .FixedOrigin();  != nil {
			return , nil
		}
	}
	return nil, nil
}

// JoinDistancedClusters incrementally pulls disconnected placement islands
// toward their common center without introducing overlap.
func ( context.Context,  *layoutgraph.Graph) error {
	if  := .Err();  != nil {
		return fmt.Errorf("PlaceNodes: %w", )
	}
	, ,  := layoutgraph.ExistingTransactionWorkGuard(, "PlaceNodes")
	if  != nil {
		return 
	}
	 := int64(len(.Nodes))
	if  {
		if  := .Add();  != nil {
			return 
		}
	}
	 := .FixedNodes()
	if len(.Nodes)-len() <= 1 {
		if  {
			return .Finish()
		}
		return nil
	}
	if len(.Edges) == 0 {
		 += int64(len(.Nodes))
		if  {
			if  := .Add(int64(len(.Nodes)));  != nil {
				return 
			}
		}
		 := false
		for ,  := range .Nodes {
			if len(.Edges) > 0 || len(.Nears) > 0 {
				 = true
				break
			}
		}
		if ! {
			if  {
				return .Finish()
			}
			return nil
		}
	}
	if ! {
		,  = limits.NewWorkGuard(, "PlaceNodes", limits.MaxTransactionWorkUnits)
		if  != nil {
			return 
		}
		if  := .Add();  != nil {
			return 
		}
	}
	var  joinPositionJournal
	 := false
	defer func() {
		if ! {
			.restore()
		}
	}()
	 := func() error {
		if  := .Finish();  != nil {
			return 
		}
		 = true
		return nil
	}
	,  := layoutgraph.Nodes(.Nodes).DistanceClustersWithWorkGuard(3*.CellSize, )
	if  != nil {
		return 
	}
	if  == nil {
		return ()
	}

	var  *geo.Point
	if len() > 0 {
		,  = layoutgraph.Nodes().CenterWithWorkGuard()
		if  != nil {
			return 
		}
	} else {
		 := make([]*geo.Point, 0, len())
		for ,  := range  {
			,  := layoutgraph.Nodes().CenterWithWorkGuard()
			if  != nil {
				return 
			}
			 = append(, )
		}
		,  = joinMedian(, )
		if  != nil {
			return 
		}
	}

	 := func( []*layoutgraph.Node,  *geo.Point) (bool, error) {
		, ,  := layoutgraph.Nodes().BoundingBoxWithWorkGuard()
		if  != nil {
			return false, 
		}
		 := geo.NewPoint(.X+(.X-.X)/2, .Y+(.Y-.Y)/2)
		 := geo.EuclideanDistance(.X, .Y, .X, .Y)
		 := math.Max(.X-.X, .Y-.Y)
		if  < /2 {
			return false, nil
		}
		var ,  float64
		 := .GetOrientation()
		if  == geo.NONE {
			return false, invariant.New("No orientation")
		}
		if  == geo.Top ||  == geo.TopLeft ||  == geo.TopRight {
			 = 1
		}
		if  == geo.Bottom ||  == geo.BottomLeft ||  == geo.BottomRight {
			 = -1
		}
		if  == geo.Left ||  == geo.BottomLeft ||  == geo.TopLeft {
			 = 1
		}
		if  == geo.Right ||  == geo.TopRight ||  == geo.BottomRight {
			 = -1
		}
		if  == 0 &&  == 0 {
			return false, nil
		}
		 *= .CellSize
		 *= .CellSize

		, ,  := false, false, false
		for ,  := range  {
			,  := .TopLeft.X+, .TopLeft.Y+
			if ! {
				if  != nil {
					if  < .X {
						,  = true, true
					}
					if  < .Y {
						,  = true, true
					}
				}
				if ! {
					var  error
					,  = .WouldOverlapWithWorkGuard(, geo.NewPoint(, ), , nil, )
					if  != nil {
						return false, 
					}
				}
			}
			if  := .moveWithChildren(, , , );  != nil {
				return false, 
			}
		}
		if  {
			for ,  := range  {
				if  := .moveWithChildren(, -, -, );  != nil {
					return false, 
				}
			}
		}
		if  != 0 &&  != 0 {
			if  && ! {
				 = false
				for ,  := range  {
					,  := .TopLeft.X, .TopLeft.Y+
					 :=  != nil && ( < .X ||  < .Y)
					if ! {
						var  error
						,  = .WouldOverlapWithWorkGuard(, geo.NewPoint(, ), , nil, )
						if  != nil {
							return false, 
						}
					}
					if  {
						 = true
					}
					if  := .moveWithChildren(, 0, , );  != nil {
						return false, 
					}
				}
				if  {
					for ,  := range  {
						if  := .moveWithChildren(, 0, -, );  != nil {
							return false, 
						}
					}
				}
			} else if ! &&  {
				 = false
				for ,  := range  {
					,  := .TopLeft.X+, .TopLeft.Y
					 :=  != nil && ( < .X ||  < .Y)
					if ! {
						var  error
						,  = .WouldOverlapWithWorkGuard(, geo.NewPoint(, ), , nil, )
						if  != nil {
							return false, 
						}
					}
					if  {
						 = true
					}
					if  := .moveWithChildren(, , 0, );  != nil {
						return false, 
					}
				}
				if  {
					for ,  := range  {
						if  := .moveWithChildren(, -, 0, );  != nil {
							return false, 
						}
					}
				}
			}
		}
		return !, nil
	}

	for range 1000 {
		if  := .Err();  != nil {
			return fmt.Errorf("PlaceNodes: %w", )
		}
		if  := .Add(int64(1 + len()));  != nil {
			return 
		}
		 := false
		for ,  := range  {
			if  := .Err();  != nil {
				return fmt.Errorf("PlaceNodes: %w", )
			}
			var  *geo.Point
			if len() > 0 {
				,  = joinContainerFixedOrigin(, [0].OwningContainer(), )
				if  != nil {
					return 
				}
			}
			,  := (, )
			if  != nil {
				return 
			}
			if  {
				 = true
			}
		}
		if ! {
			break
		}
	}
	if  := .Err();  != nil {
		return fmt.Errorf("PlaceNodes: %w", )
	}
	return ()
}