// Copyright (c) 2019 Kiel University and others.
//
// SPDX-License-Identifier: EPL-2.0

package routing

import (
	

	
	
	
)

type edgeRoutingDirection uint8

const (
	clockwise edgeRoutingDirection = iota
	counterClockwise
)

// RouteOrthogonal ports OrthogonalSelfLoopRouter from ELK 0.8.1.
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(, , , )
			.Edge(.LEdge).BendPoints = 
			for ,  := range  {
				updateMargins(.Size, &, )
			}
		}
		if .Labels != nil {
			placeOrthogonalRoutedLabels(, , , )
			updateMargins(.Size, &, .Labels.Position)
			updateMargins(.Size, &, .Labels.Position.Add(.Labels.Size))
		}
	}
	.Margin = 
}

func computeEdgeRoutingDirection( *layered.LGraphArena,  *loops.Edge) edgeRoutingDirection {
	 := .Port(.Source.LPort)
	 := .Port(.Target.LPort)
	if .Side == .Side {
		if .ID < .ID {
			return clockwise
		}
		return counterClockwise
	}
	if sideRight(.Side) == .Side {
		return clockwise
	}
	if sideLeft(.Side) == .Side {
		return counterClockwise
	}
	if .HyperLoop.OccupiedSides[sideRight(.Side)] {
		return clockwise
	}
	return counterClockwise
}

func placeOrthogonalRoutedLabels(
	 *layered.LGraphArena,
	 *loops.HyperLoop,
	 [5][]float64,
	 float64,
) {
	if hasInlineLabels(, ) {
		 = 0
	}
	placeRoutedLabels(, , )
}

func placeRoutedLabels( *loops.HyperLoop,  [5][]float64,  float64) {
	 := .Labels
	 := [.Side][.RoutingSlot[.Side]]
	switch .Side {
	case layered.NorthSide:
		.Position.Y =  -  - .Size.Y
	case layered.SouthSide:
		.Position.Y =  + 
	case layered.WestSide:
		.Position.X =  -  - .Size.X
	case layered.EastSide:
		.Position.X =  + 
	}
}

func updateMargins( geometry.Point,  *geometry.Spacing,  geometry.Point) {
	.Left = math.Max(.Left, -.X)
	.Right = math.Max(.Right, .X-.X)
	.Top = math.Max(.Top, -.Y)
	.Bottom = math.Max(.Bottom, .Y-.Y)
}

func computeRoutingSlotPositions(
	 *layered.LGraphArena,
	 *loops.Holder,
	, ,  float64,
) [5][]float64 {
	var  [5][]float64
	for  := layered.UndefinedSide;  <= layered.WestSide; ++ {
		[] = make([]float64, .RoutingSlotCount[])
	}
	initializeWithMaxLabelHeight([:], , layered.NorthSide)
	initializeWithMaxLabelHeight([:], , layered.SouthSide)
	for  := layered.NorthSide;  <= layered.WestSide; ++ {
		computeSlotPositions(, [:], , , , , )
	}
	return 
}

func initializeWithMaxLabelHeight( [][]float64,  *loops.Holder,  layered.PortSide) {
	for ,  := range .HyperLoops {
		if .Labels != nil && .Labels.Side ==  {
			 := .RoutingSlot[]
			[][] = math.Max([][], .Labels.Size.Y)
		}
	}
}

func computeSlotPositions(
	 *layered.LGraphArena,
	 [][]float64,
	 *loops.Holder,
	 layered.PortSide,
	, ,  float64,
) {
	 := baselinePosition(, , , )
	 := 1.0
	if  == layered.NorthSide ||  == layered.WestSide {
		 = -1
	}
	for ,  := range [] {
		if  > 0 {
			 += 
		}
		[][] = 
		 +=  * ( + )
	}
}

func baselinePosition(
	 *layered.LGraphArena,
	 *loops.Holder,
	 layered.PortSide,
	 float64,
) float64 {
	 := .Node(.LNode)
	switch  {
	case layered.NorthSide:
		return -.Margin.Top - 
	case layered.EastSide:
		return .Size.X + .Margin.Right + 
	case layered.SouthSide:
		return .Size.Y + .Margin.Bottom + 
	case layered.WestSide:
		return -.Margin.Left - 
	default:
		return -1
	}
}

func computeOrthogonalBendPoints(
	 *layered.LGraphArena,
	 *loops.Edge,
	 edgeRoutingDirection,
	 [5][]float64,
) geometry.Chain {
	 := make(geometry.Chain, 0, 6)
	 = append(, outerBendPoint(, , .Source, ))
	 = addCornerBendPoints(, , , , )
	 = append(, outerBendPoint(, , .Target, ))
	return 
}

func outerBendPoint(
	 *layered.LGraphArena,
	 *loops.Edge,
	 *loops.Port,
	 [5][]float64,
) geometry.Point {
	 := .Port(.LPort)
	 := baseVector(.Side, .HyperLoop.RoutingSlot[.Side], )
	 := .Position.Add(.Anchor)
	if .Side == layered.NorthSide || .Side == layered.SouthSide {
		.X += .X
	} else {
		.Y += .Y
	}
	return 
}

func addCornerBendPoints(
	 *layered.LGraphArena,
	 *loops.Edge,
	 edgeRoutingDirection,
	 [5][]float64,
	 geometry.Chain,
) geometry.Chain {
	 := .Port(.Source.LPort).Side
	 := .Port(.Target.LPort).Side
	 := .IsInline() && .HyperLoop.Labels != nil
	 := .LabelSide()
	 := geometry.Point{}
	if  {
		 = .HyperLoop.Labels.Size
	}
	for  !=  {
		 := sideLeft()
		if  == clockwise {
			 = sideRight()
		}
		 := baseVector(, .HyperLoop.RoutingSlot[], )
		 := baseVector(, .HyperLoop.RoutingSlot[], )
		if  {
			if  ==  {
				adjustVectorForLabelSide(&, , )
			} else if  ==  {
				adjustVectorForLabelSide(&, , )
			}
		}
		 = append(, .Add())
		 = 
	}
	return 
}

func adjustVectorForLabelSide(
	 *geometry.Point,
	 layered.PortSide,
	 geometry.Point,
) {
	switch  {
	case layered.NorthSide:
		.Y -= .Y / 2
	case layered.SouthSide:
		.Y += .Y / 2
	case layered.WestSide:
		.X -= .X / 2
	case layered.EastSide:
		.X += .X / 2
	}
}

func baseVector( layered.PortSide,  int,  [5][]float64) geometry.Point {
	 := [][]
	if  == layered.NorthSide ||  == layered.SouthSide {
		return geometry.Point{Y: }
	}
	return geometry.Point{X: }
}