package placement

import (
	
	
	

	
	
	
	
)

// C is the current node:
//
/*
// X
// |
// E -- A
// |    |
// D    C
//	    |
//	    B
//
// # We can tranpose C-B around A to get
//
// X
// |
// E -- A -- C -- B
// |
// D
//
// Which is more symmetrical
*/
func transpose( context.Context,  *layoutgraph.Graph,  *layoutgraph.Node,  []*layoutgraph.EdgeAbduction) (bool, error) {
	, ,  := layoutgraph.EnsureTransactionWorkGuard(, "TransposeTransactions")
	if  != 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
	}
	// Maybe re-enable when I see a use case
	for ,  := range  {
		if .OriginallyFrom ==  || .OriginallyTo ==  {
			return false, nil
		}
		if .CurrentFrom ==  || .CurrentTo ==  {
			return false, nil
		}
	}
	if len(.Edges) != 2 && len(.Edges) != 1 {
		return false, nil
	}
	,  := limits.NewWorkGuard(, "TransposeReachability", limits.MaxEngineWorkUnits)
	if  != nil {
		return false, 
	}
	 := func( *layoutgraph.Node,  bool,  map[*layoutgraph.Node]struct{}) ([]*layoutgraph.Node, error) {
		return .AllReachableNodesContext(, false, true, , )
	}

	// Need to account for this case too, whereby the container should be the one moving
	// ┌───────────┐           ┌────────────┐
	// │           │           │            │
	// │           │           │            │
	// │   ┌────┐  │           │    ┌───┐   │
	// │   │    │  │           │    │   │   │
	// │   │    ◄──┼───────────┼────┤   │   │
	// │   └────┘  │           │    └───┘   │
	// │           │           │            │
	// │           │           │            │
	// └───────────┘           └────────────┘
	//

	var  []*layoutgraph.Node
	var  *layoutgraph.Node
	if len(.Edges) == 1 {
		 := .Adjacent(.Edges[0])
		if .IsDescendantOf() || .IsDescendantOf() {
			return false, nil
		}
		if .Orientation().IsDiagonal() {
			return false, nil
		}
		 := .NearestSharedAncestor()

		 = 
		if  == nil {
			 := 
			for .OwningContainer() !=  {
				 = .OwningContainer()
			}

			,  = (, false, map[*layoutgraph.Node]struct{}{: {}})
			if  != nil {
				return false, 
			}
			if slices.ContainsFunc(, .IsDescendantOf) {
				return false, nil
			}
		} else {
			 = []*layoutgraph.Node{}
		}
	} else {
		 := .Adjacent(.Edges[0])
		 := .Adjacent(.Edges[1])

		if .IsDescendantOf() || .IsDescendantOf() {
			return false, nil
		}
		if .IsDescendantOf() || .IsDescendantOf() {
			return false, nil
		}

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

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

		 := .Orientation()
		 := .Orientation()

		if .IsDiagonal() || .IsDiagonal() {
			return false, nil
		}

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

		// Rotate the side with less nodes around the side with more nodes
		if len() >= len() {
			if  == nil {
				 := 
				for .OwningContainer() !=  {
					 = .OwningContainer()
				}

				,  = (, false, map[*layoutgraph.Node]struct{}{: {}})
				if  != nil {
					return false, 
				}
				if slices.ContainsFunc(, .IsDescendantOf) {
					return false, nil
				}
			} else {
				 = append(, )
			}

			 = 
		} else {
			if  == nil {
				 := 
				for .OwningContainer() !=  {
					 = .OwningContainer()
				}

				,  = (, false, map[*layoutgraph.Node]struct{}{: {}})
				if  != nil {
					return false, 
				}
				if slices.ContainsFunc(, .IsDescendantOf) {
					return false, nil
				}
			} else {
				 = append(, )
			}
			 = 
		}
	}
	if  := .Finish();  != nil {
		return false, 
	}

	for ,  := range  {
		if .FixedOrigin() != nil {
			return false, nil
		}
	}

	 := func() (float64, error) {
		if  == nil {
			return placementcost.EdgeLength(, , placementcost.EdgeLengthOptions{EdgeAbductions: nil, IncludeNodeSizes: true, EnforceMinimumGap: false, PenalizeDirection: true})
		}
		,  := placementcost.NodeEdgeLength(, , placementcost.EdgeLengthOptions{EdgeAbductions: , IncludeNodeSizes: true, EnforceMinimumGap: false, PenalizeDirection: true})
		if  != nil {
			return 0, 
		}
		for ,  := range .Edges {
			,  := placementcost.NodeEdgeLength(, .Adjacent(), placementcost.EdgeLengthOptions{EdgeAbductions: , IncludeNodeSizes: true, EnforceMinimumGap: false, PenalizeDirection: true})
			if  != nil {
				return 0, 
			}
			 += 
		}
		return , nil
	}

	,  := ()
	if  != nil {
		return false, 
	}
	 := -1

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

	// Rotate all around
	for  := 0;  < 3; ++ {
		.AddOp(func() error {
			for ,  := range  {
				rotateAround(, , , +1,  != nil)
				if .IsClusterVessel() {
					if ,  := optimizeCluster(, .Clusters[], true);  != nil {
						return 
					}
				}
			}
			return nil
		})

		if  := .Commit();  == nil {
			,  := ()
			if  != nil {
				.Rollback()
				.Clear()
				return false, 
			}
			if geo.PrecisionCompare(, , geo.PRECISION) < 0 {
				 = 
				 =  + 1
			}
		} else if !layoutgraph.IsCandidateRejection() {
			return false, 
		}

		.Rollback()
		.Clear()
	}

	if  != -1 {
		.AddOp(func() error {
			for ,  := range  {
				rotateAround(, , , ,  != nil)
				if .IsClusterVessel() {
					if ,  := optimizeCluster(, .Clusters[], true);  != nil {
						return 
					}
				}
			}
			return nil
		})
		if  := .Commit();  != nil {
			return false, 
		}
		return true, nil
	}

	return false, nil
}

func rotateAround( *layoutgraph.Node,  *layoutgraph.Graph,  *layoutgraph.Node,  int,  bool) {
	for  := 0;  < ; ++ {
		 := .Center()

		// Translate node center to origin
		 := .X - .TopLeft.X - .Width/2
		 := .Y - .TopLeft.Y - .Height/2

		// Rotate 90 degrees counterclockwise
		 := -
		 := 

		// Translate back
		 :=  + .TopLeft.X + .Width/2
		 :=  + .TopLeft.Y + .Height/2

		// Calculate new top-left position
		 := math.Round( - .Width/2)
		 := math.Round( - .Height/2)

		if  {
			if int()%int(.CellSize) != 0 {
				 = roundToNearestCellSize(, .CellSize)
			}
			if int()%int(.CellSize) != 0 {
				 = roundToNearestCellSize(, .CellSize)
			}
		}

		.MoveAbsWithChildren(, )
	}
}