package routing

import (
	
	
	
	

	

	
	
)

func newOVGForHierarchy( *layoutgraph.Graph,  *layoutgraph.Hierarchy,  *ovgBuildGuard) (*OVG, error) {
	if  := .check();  != nil {
		return nil, 
	}
	// only for SQL Table hierarchies
	// ports must be added before we transpose because transpose can change the node size and then the ports positions
	// for example, take the node below with 2 top/bottom ports and 1 left/right port
	// initial        ->    after transpose      ->      ports added       |       expected
	// ┌──*─────*─┐             ┌─────┐                   ┌*───*┐                  ┌──*──┐
	// *          *             |     |                   |     |                  |     |
	// └──*─────*─┘             |     |                   |     |                  *     *
	//                          |     |                   *     *                  |     |
	//                          |     |                   |     |                  *     *
	//                          |     |                   |     |                  |     |
	//                          └─────┘                   └*───*┘                  └──*──┘
	// note how adding ports after transposing creates them in the wrong position
	// this happens because ports are just relative snap points on a given side
	// in this case, adding the ports first (as in `initial` above) and then transposing them later with the node results in `expected`
	 := .Levels()
	 := make([]*layoutgraph.Node, 0, len())
	for  := range  {
		if  := .step();  != nil {
			return nil, 
		}
		 = append(, )
	}
	if  := .reserveSortWork(len());  != nil {
		return nil, 
	}
	slices.SortFunc(, func(,  *layoutgraph.Node) int {
		return cmp.Compare(.ID, .ID)
	})
	if  := .check();  != nil {
		return nil, 
	}

	 := newBuildOVG(, )
	if  := .addPorts(, );  != nil {
		return nil, 
	}

	// if the current direction is Left or Top, we need to mirror for the OVG creation, then mirror back at the very end
	switch [0].ContainerDirection() {
	case geo.Left:
		if  := .reserveHierarchyTransform(, len());  != nil {
			return nil, 
		}
		for ,  := range  {
			.Mirror(true, false)
		}
		.transformHierarchyOVGNodes(func( *OVGNode) {
			.X = -.X
		}, func( geo.Orientation) geo.Orientation {
			return mirrorPortDirection(, true, false)
		})
		defer func() {
			for ,  := range  {
				.Mirror(true, false)
			}
			.transformHierarchyOVGNodes(func( *OVGNode) {
				.X = -.X
			}, func( geo.Orientation) geo.Orientation {
				return mirrorPortDirection(, true, false)
			})
		}()
		if  := .check();  != nil {
			return nil, 
		}
	case geo.Top:
		if  := .reserveHierarchyTransform(, len());  != nil {
			return nil, 
		}
		for ,  := range  {
			.Mirror(false, true)
		}
		.transformHierarchyOVGNodes(func( *OVGNode) {
			.Y = -.Y
		}, func( geo.Orientation) geo.Orientation {
			return mirrorPortDirection(, false, true)
		})
		defer func() {
			for ,  := range  {
				.Mirror(false, true)
			}
			.transformHierarchyOVGNodes(func( *OVGNode) {
				.Y = -.Y
			}, func( geo.Orientation) geo.Orientation {
				return mirrorPortDirection(, false, true)
			})
		}()
		if  := .check();  != nil {
			return nil, 
		}
	}

	// the hierarchy must be Top to Bottom, Left to Right when creating the OVG
	// if `isHorizontalHierarchy`, the hierarchy is transposed to vertical, the OVG is created, and then transposed back
	if  := .reserveWork(uint64(len()));  != nil {
		return nil, 
	}
	 := hierarchy.IsHorizontal()
	if  {
		if  := .reserveHierarchyTransform(, len(.NodesInsideBoundingBox));  != nil {
			return nil, 
		}
		.transpose()
		defer func() {
			.transpose()
		}()
		if  := .check();  != nil {
			return nil, 
		}
	}

	if  := .addNodesIntersections(, );  != nil {
		return nil, 
	}
	if  := .addHierarchyLevelNodes(, , );  != nil {
		return nil, 
	}
	, ,  := .ovgBoundingBox()
	if  != nil {
		return nil, 
	}
	if  := .addNewBoundaryLayers(, , , );  != nil {
		return nil, 
	}
	if  := .addCornerNodes(, , , );  != nil {
		return nil, 
	}
	if  := .check();  != nil {
		return nil, 
	}

	return , nil
}

func ( *OVG) ( *layoutgraph.Graph,  *layoutgraph.Hierarchy,  *ovgBuildGuard) error {
	if  := .check();  != nil {
		return 
	}
	 := map[int][]*layoutgraph.Node{}
	for ,  := range .Levels() {
		if  := .step();  != nil {
			return 
		}
		[] = append([], )
	}
	for  := 0;  < len(); ++ {
		if  := .step();  != nil {
			return 
		}
		if  := .reserveSortWork(len([]));  != nil {
			return 
		}
		sort.Slice([], func(,  int) bool {
			return [][].TopLeft.X < [][].TopLeft.X
		})
	}
	, ,  := .ovgBoundingBox()
	if  != nil {
		return 
	}
	// Align the bounds to ovgPadding so OVG nodes line up across levels.
	// Subtract one interval to leave at least one OVG node before the first node.
	.X -= ovgPadding
	.X = math.Floor(.X/ovgPadding) * ovgPadding
	 := math.Ceil((.X-.X)/ovgPadding) * ovgPadding
	// Add one interval to leave at least one OVG node after the last node.
	.X = .X +  + ovgPadding
	 := make(map[float64]struct{})
	for  := 1;  < len(); ++ {
		if  := .step();  != nil {
			return 
		}
		,  := .addBoundaryNodesAboveLevelNodes([-1], [], .X, .X, )
		if  != nil {
			return 
		}
		if  := .addPortAlignedNodes([-1], [], , );  != nil {
			return 
		}
		for ,  := range  {
			[] = struct{}{}
		}
	}

	,  := .addLevelNodes(, .X, .X, )
	if  != nil {
		return 
	}
	return .addIntersections(, , , )
}

// addBoundaryNodesAboveLevelNodes adds nodes between two levels at the graph boundaries.
// Given the graph below with OVG Nodes highlighted as `*`, this routine will
// add new OVG Nodes, highlighted as `#`, between levels at the left and right boundaries of the OVG.
// Note: boundary nodes are padded considering the "tl, br" change in `ovg.forHierarchicalGraph`
//
// .     ┌──*───────*───┐         ┌──*───────*───┐
// .     │              │         │              │
// .     *              *         *              *
// .     │              │         │              │
// .     *              *         *              *
// .     │              │         │              │
// .     └──*───────*───┘         └──*───────*───┘
// .
// . #                                               #
// .
// . #                                               #
// .
// .                  ┌──*───────*───┐
// .                  │              │
// .                  *              *
// .                  │  nodesBelow  │
// .                  *              *
// .                  │              │
// .                  └──*───────*───┘
func ( *OVG) (,  []*layoutgraph.Node, ,  float64,  *ovgBuildGuard) ([]float64, error) {
	if  := .check();  != nil {
		return nil, 
	}
	 := math.Inf(1)
	 := 1.0
	for ,  := range  {
		if  := .step();  != nil {
			return nil, 
		}
		// top is the closest to 0, 0 in SVG space
		 = math.Min(, .TopLeft.Y)
		for ,  := range .Edges {
			if  := .step();  != nil {
				return nil, 
			}
			 := .Adjacent()
			if .IsLoop() || .Hierarchy == nil {
				// hierarchical nodes can be connected to nodes outside the hierarchy
				continue
			}
			if .HierarchyLevel() < .HierarchyLevel() {
				++
			}
		}
	}

	 := math.Inf(-1)
	for ,  := range  {
		if  := .step();  != nil {
			return nil, 
		}
		 = math.Max(, .TopLeft.Y+.Height)
	}

	 := ( - )
	 :=  / ovgPadding
	 := math.Min(, )
	 := math.Ceil( / )
	 = math.Max(, ovgPadding)

	// these will form the horizontal lines above `nodesBelow`
	var  []float64
	 := 
	for  := 0;  < int()-1; ++ {
		if  := .step();  != nil {
			return nil, 
		}
		 -= 
		 = append(, )
		if ,  := .addPoint(, geo.NewPoint(, ));  != nil {
			return nil, 
		}
		if ,  := .addPoint(, geo.NewPoint(, ));  != nil {
			return nil, 
		}
	}
	return , .check()
}

// addPortAlignedNodes adds OVG Nodes aligned with ports (up/down) at the Y coordinates of the level (marked as `#` below)
// .      #       #                #       #
// .   ┌──*───────*───┐         ┌──*───────*───┐
// .   │              │         │              │
// .   *              *         *              *
// .   │              │         │              │
// .   *              *         *              *
// .   │              │         │              │
// .   └──*───────*───┘         └──*───────*───┘
func ( *OVG) (,  []*layoutgraph.Node,  []float64,  *ovgBuildGuard) error {
	if  := .check();  != nil {
		return 
	}
	for ,  := range  {
		if  := .step();  != nil {
			return 
		}
		for ,  := range  {
			if  := .step();  != nil {
				return 
			}
			,  := .portsByOrientation(, , geo.Bottom)
			if  != nil {
				return 
			}
			for ,  := range  {
				if ,  := .addPoint(, geo.NewPoint(.X, ));  != nil {
					return 
				}
			}
		}
		for ,  := range  {
			if  := .step();  != nil {
				return 
			}
			,  := .portsByOrientation(, , geo.Top)
			if  != nil {
				return 
			}
			for ,  := range  {
				if ,  := .addPoint(, geo.NewPoint(.X, ));  != nil {
					return 
				}
			}
		}
	}
	return .check()
}

// addLevelNodes creates port aligned nodes between ordered siblings.
// Returns the X coordinate of added nodes.
// .    ┌──*───────*───┐         ┌──*───────*───┐
// .    │              │         │              │
// . +  *              │    +    *              * +
// .    │              *    +    │              │
// . +  *              │    +    *              * +
// .    │              │         │              │
// .    └──*───────*───┘         └──*───────*───┘
func ( *OVG) ( map[int][]*layoutgraph.Node, ,  float64,  *ovgBuildGuard) (map[float64]struct{}, error) {
	if  := .check();  != nil {
		return nil, 
	}
	 := make(map[float64]struct{})
	for  := 0;  < len(); ++ {
		if  := .step();  != nil {
			return nil, 
		}
		 := false
		 := 0
		 := [][]
		,  := .portsByOrientation(, , geo.Left)
		if  != nil {
			return nil, 
		}
		for  := ;  <= ;  += ovgPadding {
			if  := .step();  != nil {
				return nil, 
			}
			[] = struct{}{}
			for ,  := range  {
				if ,  := .addPoint(, geo.NewPoint(, .Y));  != nil {
					return nil, 
				}
			}
			if ! &&  >= .TopLeft.X-ovgPadding {
				// set `x` as the next X coordinate aligned with "common OVG nodes" (see the method docs)
				 := .TopLeft.X + .Width
				 = math.Ceil(/ovgPadding) * ovgPadding
				// now use the ports on the right of this node
				,  = .portsByOrientation(, , geo.Right)
				if  != nil {
					return nil, 
				}
				 =  == len([])-1
				if ! {
					// if there is other node to the right, use the left ports of this node too
					++
					 = [][]
					,  := .portsByOrientation(, , geo.Left)
					if  != nil {
						return nil, 
					}
					 = append(, ...)
				}
			}
		}
	}
	return , .check()
}

func ( *OVG) () {
	// Transpose each canonical OVG point and each owner's complete direction
	// set once. Ports may appear multiple times in Ports when rounded snap
	// points coincide, so iterating those slices would toggle shared points and
	// metadata more than once.
	.transformHierarchyOVGNodes(func( *OVGNode) {
		.X, .Y = .Y, .X
	}, transposePortDirection)
	for ,  := range .NodesInsideBoundingBox {
		.Transpose()
	}
}

func ( *OVG) ( func(*OVGNode),  func(geo.Orientation) geo.Orientation) {
	for ,  := range .Nodes {
		()
		for ,  := range .portOwners() {
			.setPortDirections(, .directions.transformed())
		}
	}
	.reindexOccupiedPoints()
}

func transposePortDirection( geo.Orientation) geo.Orientation {
	switch  {
	case geo.Top:
		return geo.Left
	case geo.Bottom:
		return geo.Right
	case geo.Left:
		return geo.Top
	case geo.Right:
		return geo.Bottom
	case geo.TopRight:
		return geo.BottomLeft
	case geo.BottomLeft:
		return geo.TopRight
	default:
		return 
	}
}

func mirrorPortDirection( geo.Orientation, ,  bool) geo.Orientation {
	if  {
		switch  {
		case geo.Left:
			 = geo.Right
		case geo.Right:
			 = geo.Left
		case geo.TopLeft:
			 = geo.TopRight
		case geo.TopRight:
			 = geo.TopLeft
		case geo.BottomLeft:
			 = geo.BottomRight
		case geo.BottomRight:
			 = geo.BottomLeft
		}
	}
	if  {
		switch  {
		case geo.Top:
			 = geo.Bottom
		case geo.Bottom:
			 = geo.Top
		case geo.TopLeft:
			 = geo.BottomLeft
		case geo.TopRight:
			 = geo.BottomRight
		case geo.BottomLeft:
			 = geo.TopLeft
		case geo.BottomRight:
			 = geo.TopRight
		}
	}
	return 
}