// 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 (
	

	
	
	
)

const (
	polylineCornerDistance  = 10.0
	polylineCornerTolerance = 0.01
)

// RoutePolyline ports ELK 0.8.1's PolylineSelfLoopRouter. It starts with the
// orthogonal self-loop skeleton and replaces every corner with two points.
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(, , , )
			 := polylineSelfLoopBendPoints(, , )
			.Edge(.LEdge).BendPoints = 
			for ,  := range  {
				updateMargins(.Size, &, )
			}
		}
		if .Labels != nil {
			placeRoutedLabels(, , )
			updateMargins(.Size, &, .Labels.Position)
			updateMargins(.Size, &, .Labels.Position.Add(.Labels.Size))
		}
	}
	.Margin = 
}

func polylineSelfLoopBendPoints(
	 *layered.LGraphArena,
	 *loops.Edge,
	 geometry.Chain,
) geometry.Chain {
	 := .Port(.Source.LPort)
	 := .Port(.Target.LPort)
	 := make(geometry.Chain, 0, len()+2)
	 = append(, .Position.Add(.Anchor))
	 = append(, ...)
	 = append(, .Position.Add(.Anchor))
	return CutPolylineCorners(, polylineCornerDistance)
}

// CutPolylineCorners ports PolylineSelfLoopRouter.cutCorners. The first and
// last points are endpoints and are intentionally excluded from the result.
func ( geometry.Chain,  float64) geometry.Chain {
	if len() <= 2 {
		return nil
	}
	 := make(geometry.Chain, 0, 2*(len()-2))
	for  := 1; +1 < len(); ++ {
		, ,  := [-1], [], [+1]
		 := nearZeroToZero(.Sub())
		 := nearZeroToZero(.Sub())

		 := 
		 = math.Min(, math.Abs(.X+.Y)/2)
		 = math.Min(, math.Abs(.X+.Y)/2)
		 = geometry.Point{
			X: javaSignum(.X) * ,
			Y: javaSignum(.Y) * ,
		}
		 = geometry.Point{
			X: javaSignum(.X) * ,
			Y: javaSignum(.Y) * ,
		}
		 = append(, .Add(), .Add())
	}
	return 
}

func nearZeroToZero( geometry.Point) geometry.Point {
	if .X >= -polylineCornerTolerance && .X <= polylineCornerTolerance {
		.X = 0
	}
	if .Y >= -polylineCornerTolerance && .Y <= polylineCornerTolerance {
		.Y = 0
	}
	return 
}

func javaSignum( float64) float64 {
	if  > 0 {
		return 1
	}
	if  < 0 {
		return -1
	}
	return 
}