// Copyright (c) 2019 Kiel University and others.
// Copyright (c) 2026 The elk-go Authors.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0.
//
// SPDX-License-Identifier: EPL-2.0

package routing

import (
	
	
	
	
)

// RouteSpline uses ELK's orthogonal self-loop skeleton and inserts the
// offset intermediate controls consumed by its cubic NUB conversion.
func ( *layered.LGraphArena,  *loops.Holder) {
	 := .Node(.LNode)
	 := loops.IndividualOrInherited(, .LNode, layered.SpacingEdgeEdge)
	 := loops.IndividualOrInherited(, .LNode, layered.SpacingEdgeLabel)
	 := loops.IndividualOrInherited(, .LNode, layered.SpacingNodeSelfLoop)
	 := .Margin
	 := computeRoutingSlotPositions(
		, , , , )

	for ,  := range .HyperLoops {
		for ,  := range .Edges {
			 := computeEdgeRoutingDirection(, )
			 := computeOrthogonalBendPoints(, , , )
			 := splineSelfLoopControls(, , , , )
			 := p5edges.CubicNUBToBezier()
			.Edge(.LEdge).BendPoints = 
			for ,  := range  {
				updateMargins(.Size, &, )
			}
		}
		if .Labels != nil {
			placeRoutedLabels(, , )
			updateMargins(.Size, &, .Labels.Position)
			updateMargins(.Size, &, .Labels.Position.Add(.Labels.Size))
		}
	}
	.Margin = 
}

func splineSelfLoopControls(
	 *layered.LGraphArena,
	 *loops.Edge,
	 edgeRoutingDirection,
	 geometry.Chain,
	 float64,
) geometry.Chain {
	 := .Port(.Source.LPort)
	 := .Port(.Target.LPort)
	 := geometry.Chain{.Position.Add(.Anchor)}
	 := .Side
	 := [0]
	for ,  := range [1:] {
		 = append(, )
		 := geometry.Point{X: (.X + .X) / 2, Y: (.Y + .Y) / 2}
		 := splinePortDirection()
		.X += .X * 
		.Y += .Y * 
		 = append(, )
		 = 
		if  == clockwise {
			 = sideRight()
		} else {
			 = sideLeft()
		}
	}
	 = append(, [len()-1])
	 = append(, .Position.Add(.Anchor))
	return 
}

func splinePortDirection( layered.PortSide) geometry.Point {
	switch  {
	case layered.NorthSide:
		return geometry.Point{Y: -1}
	case layered.EastSide:
		return geometry.Point{X: 1}
	case layered.SouthSide:
		return geometry.Point{Y: 1}
	case layered.WestSide:
		return geometry.Point{X: -1}
	default:
		return geometry.Point{}
	}
}