package placement

import (
	
	
	
	
	

	
	
	
	
	
	
	
	
)

// placeNodes is phase 1 of the algorithm
func placeNodes( context.Context,  *layoutgraph.Graph,  *layoutgraph.Node,  int64,  []*layoutgraph.EdgeAbduction,  []geo.Box) error {
	// Edge cases
	if len(.Nodes) == 0 {
		return nil
	}

	if len(.Nodes) == 1 {
		 := .Nodes[0]
		if .FixedTopLeft != nil {
			.TopLeft = .FixedTopLeft.Copy()
		} else {
			.TopLeft = geo.NewPoint(0, 0)
		}
		.SyncNestedGeometry()
		// there may be trees that need to be reconnected to this node
		if  := trees.Place(, , );  != nil {
			return 
		}
		return direct(, , .Nodes, , directOptions{})
	}

	// This graph is one undergoing node placement in this call
	 := layoutgraph.NewGraph()
	.CopyEntitiesFrom()

	// Add every direct child of the current root to the new graph
	for ,  := range .Containers[] {
		.AddNodeUnchecked()
	}
	if len(.Nodes) == 0 {
		return nil
	}

	// When performing layout for a fixed child A, if child B is also fixed it creates an obstacle to try to avoid for A's children layout
	// .     ┌─A@(10,0)───────────────────┐  │    ┌─A@(10,0)─┐
	// .     │                            │  │    │          │
	// .     │  ┌────┐   ┌────┐   ┌────┐  │  │    │  ┌────┐  │
	// .     │  │    ├───►    ├───►    │  │  │    │  │    │  │
	// .     │  └────┘   └────┘   └────┘  │  │    │  └─┬──┘  │
	// .     │                            │  │    │  ┌─▼──┐  │
	// .     └────────────────────────────┘  │    │  │    │  │
	// .                                     │    │  └──┬─┘  │
	// . ┌─B@(0,100)─┐                       │ ┌─B@(0,100)─┐ │
	// . │           │                       │ │  │  ┌──▼─┐│ │
	// . │           │ A's PREFERRED LAYOUT  │ │  │  │    ││ │ A's LAYOUT TO AVOID
	// . │           │                       │ │  │  └────┘│ │
	// . └───────────┘                       │ └──└────────┘─┘
	// we want to pass each child these other obstacles to try to avoid
	// Note: it isn't always possible to avoid overlaps depending on the fixed positions
	// Note: we don't know final sizes of A and B (dependencies: B size -> B children layout -> A size -> A children layout -> B size -> ...)
	var  []geo.Box
	for ,  := range layoutgraph.Nodes(.Containers[]).FixedNodes() {
		// TODO improve by computing minimum width/height (width >= widest child + padding, width >= rightmost fixed child)
		 := geo.Box{TopLeft: .FixedTopLeft.Copy(), Width: .Width, Height: .Height}
		 = append(, )
	}

	// we want to take the ancestor obstacles for layout at this level, and translate them to be relative to this root node
	if  != nil && .FixedTopLeft != nil && len() > 1 {
		,  := -.FixedTopLeft.X-layoutgraph.ContainerPadding, -.FixedTopLeft.Y-layoutgraph.ContainerPadding
		if  != 0 ||  != 0 {
			for ,  := range  {
				.TopLeft.X += 
				.TopLeft.Y += 
			}
			defer func() {
				for ,  := range  {
					.TopLeft.X -= 
					.TopLeft.Y -= 
				}
			}()
		}
	}

	 := .AbductEdges(, )
	 := false
	defer func() {
		if  {
			return
		}
		.RestoreEdgeAbductions()
		layoutgraph.Nodes(.Nodes).SetGraphReference()
	}()

	,  := PlaceChildrenOrder(, .Containers[], )
	if  != nil {
		return 
	}
	for ,  := range  {
		if .Hierarchy != nil {
			continue
		}
		// If the direct child is a container with more children, then recurse first
		// So we go from most nested to least (root)
		if .IsContainer() {
			if  := (, , , , , );  != nil {
				return 
			}
			loops.UpdateOffsets()
			// descendant may have placed trees so we need to update containers map
			.Containers = .Containers
		}
		if .IsClusterVessel() {
			for ,  := range .Clusters[].Nodes {
				if .IsContainer() {
					if  := (, , , , , );  != nil {
						return 
					}
					.Containers = .Containers
				}
			}
		}
	}

	// we don't know how the subgraphs will be combined (unless it is only one),
	// but we can try to layout each subgraph assuming it is the only one and therefore it should try to avoid fixed ancestors
	// then when we combine subgraphs, we can also avoid fixed ancestors

	if  := proximity.AssignNears(, , , );  != nil {
		return 
	}
	if  := proximity.AssignHerds(, , , );  != nil {
		return 
	}
	, ,  := .SplitSubgraphsTracked(, layoutgraph.SplitOptions{
		IncludeNears:  true,
		TraverseTrees: true,
	}, nil)
	if  != nil {
		return 
	}
	 := false
	defer func() {
		if ! {
			// SplitSubgraphs redirects every reachable node to a temporary graph,
			// including Near-only nodes outside g.Nodes. Restore their exact prior
			// owners if any later placement work fails or panics.
			.Restore()
		}
	}()
	for ,  := range  {
		.ComputeCellSize()
		layoutgraph.

			// Put all the nested nodes Graph reference as the subgraph
			// There are calls that use the node's graph reference
			Nodes(.Nodes).SetGraphReference()

		// TODO: this must be a better check, like only select the nodes that must be placed
		// probably, we want to skip placing nodes that were already placed
		if .Nodes[0].Hierarchy == nil {
			 := rand.New(rand.NewSource())
			if  := placeNodesOrthogonally(, , , , , , );  != nil {
				return 
			}
			for ,  := range .Nodes {
				if .IsClusterVessel() {
					if ,  := optimizeCluster(, .Clusters[], true);  != nil {
						return 
					}
				}
			}
		}

		if  := trees.Place(, , );  != nil {
			return 
		}
		// Tree nodes and edges have been restored within subgraph but we need to also add these back to g
		for ,  := range .Nodes {
			for ,  := range .Trees[] {
				for ,  := range append(trees.Descendants(), ) {
					.AddNodeUnchecked(.Node)
					.AddEdge(.SentinelEdge)
				}
			}
		}
		// reconnectTree restores the node to subgraph.Containers[root] but if the slice grows from an append,
		// subgraph.Containers[root] will be a different slice than g.Containers[root] so we need to sync the entry in g.Containers
		.Containers[] = .Containers[]
		for ,  := range  {
			.Containers[] = .Containers[]
		}

		// To determine within mirroring which container contents are reachable and should be recursively mirrored,
		// the edges need to be temporarily restored
		.RestoreEdgeAbductions()
		 := direct(, , .Nodes, , directOptions{})
		.ApplyEdgeAbductions()
		if  != nil {
			return 
		}

		if  := validatePlacedNodes(, .Nodes);  != nil {
			return 
		}
	}
	,  := packing.CombineSubgraphs(, , , )
	if  != nil {
		return 
	}

	if  := orientSourceInterior(, , , );  != nil {
		return 
	}
	// Set the dimensions of the root according to outcome of node placement
	if  != nil {
		.FitToGraph(, .ContainerPadding(, false))
		// The root may be a cluster node, in which case all the other cluster nodes have to also be resized
		for ,  := range .Clusters {
			 := slices.Contains(.Nodes, )
			if  {
				.Resize(.Vessel)
			}
		}
	}

	.RestoreEdgeAbductions()
	layoutgraph.Nodes(.Nodes).SetGraphReference()
	 = true
	 = true

	return nil
}

// This routine is heavily based on the algorithm proposed in Graph Compact Orthogonal Layout Algorithm by Freivalds and Glagolevs
func placeNodesOrthogonally( context.Context,  *layoutgraph.Node,  *layoutgraph.Graph,
	 []*layoutgraph.EdgeAbduction,  *rand.Rand,
	 []geo.Box,  ...int64) error {
	 := .FixedNodes()
	 := false
	var  error
	if len() > 0 && [0]%2 == 0 {
		,  = initializeByGraphDistance(, )
	}
	if  == nil && ! {
		 = initializeNodes(, )
	}
	if  != nil {
		return 
	}
	// After nodes in this nesting level have been initialized, the inner cluster nodes and container children need to also move
	.SyncNestedGeometry()

	 := int(float64(nodePlacementIterations) * math.Sqrt(float64(len(.Nodes))))
	 := 2 * math.Sqrt(float64(len(.Nodes)))
	 := math.Pow(0.2/, 1.0/float64())

	// run compaction every 9 iterations alternating between horizontal and vertical compaction
	 := 9
	 := horizontalAxis

	,  := newSizelessOptimizer(, , )
	if  != nil {
		return 
	}
	for  := 0;  < /2; ++ {
		if  := .Err();  != nil {
			return fmt.Errorf("PlaceNodes: %w", )
		}
		if  := .optimize(, );  != nil {
			return 
		}

		if ( % ) == 0 {
			if  := compaction(, , compactionOptions{
				axis:   ,
				factor: compactionFactor,
			});  != nil {
				return 
			}
			 = .opposite()
			.resetOccupied()
		}
		 =  * 
	}

	if  := compaction(, , compactionOptions{
		edgeAbductions: ,
		axis:           horizontalAxis,
		includeSizes:   true,
		factor:         compactionFactor,
		transition:     true,
	});  != nil {
		return 
	}
	if  := compaction(, , compactionOptions{
		edgeAbductions: ,
		axis:           verticalAxis,
		includeSizes:   true,
		factor:         compactionFactor,
		transition:     true,
	});  != nil {
		return 
	}

	// if there are fixed nodes, we want to shift them to their fixed top left positions
	if len() > 0 {
		 := [0].FixedTopLeft.X - [0].TopLeft.X
		 := [0].FixedTopLeft.Y - [0].TopLeft.Y
		for ,  := range .Nodes {
			.MoveWithChildren(, )
		}
		for ,  := range  {
			// adjust precisely ignoring cell size alignment
			.MoveAbsWithChildren(.FixedTopLeft.X, .FixedTopLeft.Y)
		}
	}

	if  := validateCellSize();  != nil {
		return 
	}
	for ,  := range .Nodes {
		if .FixedTopLeft != nil {
			continue
		}

		 := .TopLeft.X
		 := .TopLeft.Y
		if math.Mod(.TopLeft.X, .CellSize) != 0 {
			 = roundToPreviousCellSize(.TopLeft.X, .CellSize)
		}
		if math.Mod(.TopLeft.Y, .CellSize) != 0 {
			 = roundToPreviousCellSize(.TopLeft.Y, .CellSize)
		}
		if  != .TopLeft.X ||  != .TopLeft.Y {
			.MoveAbsWithChildren(, )
		}
	}

	if  := validateGridAlignment();  != nil {
		return 
	}

	// The turn cost is cached for edge length estimates, and we compute here
	.TurnCost()
	// Since the compaction factor is rather high, we estimate the overall maxLength would actually be ~half it
	.HalveTurnCost()

	,  := newSizedOptimizer(, , , , , )
	if  != nil {
		return 
	}

	proximity.SyncHerdFences()
	for  := ( / 2) + 1;  < ; ++ {
		if  := .Err();  != nil {
			return fmt.Errorf("PlaceNodes: %w", )
		}
		,  := .optimize(, )
		if  != nil {
			return 
		}

		if ( % ) == 0 {
			 := math.Max(1.0, 1.0+(2.0*float64(--30)/(0.5*float64())))
			if  := compaction(, , compactionOptions{
				edgeAbductions: ,
				axis:           ,
				includeSizes:   true,
				factor:         ,
			});  != nil {
				return 
			}
			 = .opposite()

			if  := grouping.JoinDistancedClusters(, );  != nil {
				return 
			}
			proximity.SyncHerdFences()
		}
		 =  * 
	}
	if  := grouping.JoinDistancedClusters(, );  != nil {
		return 
	}
	proximity.SyncHerdFences()

	for  := 0;  < 10; ++ {
		,  := .optimize(, 0.0)
		if  != nil {
			return 
		}
		if ! {
			break
		}
	}

	if len() > 0 {
		 := [0].FixedTopLeft.X - [0].TopLeft.X
		 := [0].FixedTopLeft.Y - [0].TopLeft.Y
		for ,  := range .Nodes {
			.MoveWithChildren(, )
		}
		for ,  := range  {
			// adjust precisely ignoring cell size alignment
			.TopLeft = .FixedTopLeft.Copy()
		}
	}
	return nil
}

func validatePlacedNodes( *layoutgraph.Node,  []*layoutgraph.Node) error {
	for ,  := range  {
		if .TopLeft == nil {
			return invariant.Errorf("node %d was not placed under container %d", .ID, .IDValue())
		}
	}
	return nil
}

func validateGridAlignment( *layoutgraph.Graph) error {
	if  := validateCellSize();  != nil {
		return 
	}
	for ,  := range .Nodes {
		if .FixedTopLeft != nil {
			continue
		}
		if .TopLeft == nil {
			return invariant.Errorf("node %d has no position", .ID)
		}
		if math.Mod(.TopLeft.X, .CellSize) != 0 || math.Mod(.TopLeft.Y, .CellSize) != 0 {
			return invariant.Errorf(
				"node %d at (%v, %v) is not aligned to cell size %v",
				.ID,
				.TopLeft.X,
				.TopLeft.Y,
				.CellSize,
			)
		}
	}
	return nil
}

func validateCellSize( *layoutgraph.Graph) error {
	if .CellSize < 1 || math.IsNaN(.CellSize) || math.IsInf(.CellSize, 0) || math.Trunc(.CellSize) != .CellSize {
		return invariant.Errorf("invalid cell size %v", .CellSize)
	}
	return nil
}
func ( context.Context,  []*layoutgraph.Node,  []*layoutgraph.EdgeAbduction) ([]*layoutgraph.Node, error) {
	if  := .Err();  != nil {
		return nil, fmt.Errorf("PlaceChildrenOrder: %w", )
	}
	 := make(map[*layoutgraph.Node]struct{}, len())
	 := make(map[*layoutgraph.Node]map[*layoutgraph.Node]struct{}, len())
	for ,  := range  {
		if  := .Err();  != nil {
			return nil, fmt.Errorf("PlaceChildrenOrder: %w", )
		}
		if  == nil {
			return nil, invariant.New("container has a nil child")
		}
		if ,  := [];  {
			return nil, invariant.Errorf("container has duplicate child %s", .DebugID())
		}
		[] = struct{}{}
		[] = make(map[*layoutgraph.Node]struct{})
	}
	for ,  := range  {
		if  := .Err();  != nil {
			return nil, fmt.Errorf("PlaceChildrenOrder: %w", )
		}
		if  == nil {
			return nil, invariant.New("child ordering has a nil edge abduction")
		}
		 := .CurrentFrom
		 := .CurrentTo
		if ,  := [];  {
			if ,  := [];  {
				[][] = struct{}{}
			}
		}
		if ,  := [];  {
			if ,  := [];  {
				[][] = struct{}{}
			}
		}
	}

	 := make([]*layoutgraph.Node, 0, len())
	 := make(map[*layoutgraph.Node]struct{}, len())
	 := func( *layoutgraph.Node) {
		 = append(, )
		[] = struct{}{}
		delete(, )
	}
	for ,  := range  {
		if  := .Err();  != nil {
			return nil, fmt.Errorf("PlaceChildrenOrder: %w", )
		}
		if len([]) == 0 {
			()
		}
	}

	for len() < len() {
		if  := .Err();  != nil {
			return nil, fmt.Errorf("PlaceChildrenOrder: %w", )
		}
		 := len() + 1
		var  *layoutgraph.Node
		for ,  := range  {
			,  := []
			if  && len() <  {
				 = len()
				 = 
			}
		}
		if  == nil {
			return nil, invariant.New("could not order all container children")
		}

		 := make(map[*layoutgraph.Node]struct{})
		 := []*layoutgraph.Node{}
		for len() > 0 {
			if  := .Err();  != nil {
				return nil, fmt.Errorf("PlaceChildrenOrder: %w", )
			}
			 := [0]
			 = [1:]
			if ,  := [];  {
				continue
			}
			[] = struct{}{}
			if ,  := [];  {
				continue
			}
			()
			for ,  := range  {
				var  *layoutgraph.Node
				if .CurrentFrom ==  {
					 = .CurrentTo
				} else if .CurrentTo ==  {
					 = .CurrentFrom
				}
				if ,  := [];  {
					 = append(, )
				}
			}
		}
	}
	if  := .Err();  != nil {
		return nil, fmt.Errorf("PlaceChildrenOrder: %w", )
	}
	return , nil
}

// syncHerdFences sets the Val of the herd to the bounds of the graph
// .   ┌─────┐
// .   │     │
// .   │     │
// .   │     │
// .   └─────┘