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

package routing

import (
	
	

	
	
	
)

const (
	unconnectedPortPenalty = 1
	connectedPortPenalty   = 3
)

type director struct {
	portPenalties []int
}

// DetermineLoopRoutes chooses the clockwise interval occupied by each loop.
func ( *layered.LGraphArena,  *loops.Holder) error {
	 := &director{}
	for ,  := range .Node(.LNode).Ports {
		.Port().ID = 
	}
	for ,  := range .HyperLoops {
		sort.SliceStable(.Ports, func(,  int) bool {
			return .Port(.Ports[].LPort).ID < .Port(.Ports[].LPort).ID
		})
	}

	for ,  := range .HyperLoops {
		switch .Type {
		case loops.OneSide:
			 := .Port(.Ports[0].LPort).Side
			.assignBoundaryPorts(, , , )
		case loops.TwoSidesCorner:
			 := ordering.SortedTwoSideLoopPortSides()
			.assignBoundaryPorts(, , [0], [1])
		case loops.TwoSidesOpposing:
			.determineOpposing(, )
		case loops.ThreeSides:
			.determineThreeSides(, )
		case loops.FourSides:
			.determineFourSides(, )
		default:
			return fmt.Errorf("unsupported self-loop type")
		}
		if .LeftmostPort == nil || .RightmostPort == nil {
			return fmt.Errorf("self-loop route has no boundary ports")
		}
		 := .Port(.LeftmostPort.LPort).Side
		 := .Port(.RightmostPort.LPort).Side
		for  !=  {
			.OccupiedSides[] = true
			 = sideRight()
		}
		.OccupiedSides[] = true
	}
	return nil
}

func ( *director) ( *layered.LGraphArena,  *loops.HyperLoop) {
	 := .PortSides()
	 := .lowest(, , [0])
	 := .highest(, , [1])
	 := .edgePenalty(, .Holder, , )
	 := .lowest(, , [1])
	 := .highest(, , [0])
	 := .edgePenalty(, .Holder, , )
	if  <=  {
		.LeftmostPort, .RightmostPort = , 
	} else {
		.LeftmostPort, .RightmostPort = , 
	}
}

func ( *director) ( *layered.LGraphArena,  *loops.HyperLoop) {
	var ,  layered.PortSide
	switch {
	case !.HasPortsOnSide(layered.NorthSide):
		,  = layered.EastSide, layered.WestSide
	case !.HasPortsOnSide(layered.EastSide):
		,  = layered.SouthSide, layered.NorthSide
	case !.HasPortsOnSide(layered.SouthSide):
		,  = layered.WestSide, layered.EastSide
	default:
		,  = layered.NorthSide, layered.SouthSide
	}
	.assignBoundaryPorts(, , , )
}

func ( *director) ( *layered.LGraphArena,  *loops.HyperLoop) {
	 := .Ports
	,  := [len()-1], [0]
	 := .edgePenalty(, .Holder, , )
	for  := 1;  < len(); ++ {
		,  := [-1], []
		 := .edgePenalty(, .Holder, , )
		if  >  {
			, ,  = , , 
		}
	}
	// The selected gap is precisely the part not traversed by the loop.
	.LeftmostPort, .RightmostPort = , 
}

func ( *director) (
	 *layered.LGraphArena,
	 *loops.HyperLoop,
	,  layered.PortSide,
) {
	.LeftmostPort = .lowest(, , )
	.RightmostPort = .highest(, , )
}

func ( *director) ( *layered.LGraphArena,  *loops.HyperLoop,  layered.PortSide) *loops.Port {
	 := .PortsBySide[][0]
	for ,  := range .PortsBySide[][1:] {
		if .Port(.LPort).ID < .Port(.LPort).ID {
			 = 
		}
	}
	return 
}

func ( *director) ( *layered.LGraphArena,  *loops.HyperLoop,  layered.PortSide) *loops.Port {
	 := .PortsBySide[][0]
	for ,  := range .PortsBySide[][1:] {
		if .Port(.LPort).ID > .Port(.LPort).ID {
			 = 
		}
	}
	return 
}

func ( *director) (
	 *layered.LGraphArena,
	 *loops.Holder,
	,  *loops.Port,
) int {
	if .portPenalties == nil {
		.computePenalties(, )
	}
	 := len(.Node(.LNode).Ports)
	 := .Port(.LPort).ID
	 := .Port(.LPort).ID - 1
	if  < 0 {
		 =  - 1
	}
	if  <=  {
		return .portPenalties[] - .portPenalties[]
	}
	return .portPenalties[-1] - .portPenalties[] + .portPenalties[]
}

func ( *director) ( *layered.LGraphArena,  *loops.Holder) {
	 := .Node(.LNode).Ports
	.portPenalties = make([]int, len())
	 := 0
	for ,  := range  {
		 := .Port()
		if len(.IncomingEdges) == 0 && len(.OutgoingEdges) == 0 {
			 += unconnectedPortPenalty
		} else {
			 += connectedPortPenalty
		}
		.portPenalties[] = 
	}
}

func sideRight( layered.PortSide) layered.PortSide {
	switch  {
	case layered.NorthSide:
		return layered.EastSide
	case layered.EastSide:
		return layered.SouthSide
	case layered.SouthSide:
		return layered.WestSide
	case layered.WestSide:
		return layered.NorthSide
	default:
		return layered.UndefinedSide
	}
}

func sideLeft( layered.PortSide) layered.PortSide {
	switch  {
	case layered.NorthSide:
		return layered.WestSide
	case layered.WestSide:
		return layered.SouthSide
	case layered.SouthSide:
		return layered.EastSide
	case layered.EastSide:
		return layered.NorthSide
	default:
		return layered.UndefinedSide
	}
}