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

package routing

import (
	

	basegraph 
	
	
	
)

type slotAssigner struct {
	arena        *layered.LGraphArena
	segments     []*orthogonal.HyperEdgeSegment
	loopSegments map[*loops.HyperLoop]*orthogonal.HyperEdgeSegment
	activity     map[*loops.HyperLoop][]bool
}

func ( *layered.LGraphArena,  *loops.Holder) {
	 := &slotAssigner{arena: }
	 := .computeLabelCrossingMatrix()
	.createCrossingGraph(, , )
	 := basegraph.Get(&.Graph(.NodeGraph(.LNode)).Properties, layered.Random)
	orthogonal.BreakNonCriticalCycles(.segments, )
	.assignRawSlotsToSegments()
	.assignRawSlotsToLoops()
	.shiftTowardsNode(, , )
}

func ( *slotAssigner) ( *loops.Holder) [][]bool {
	 := 0
	for ,  := range .HyperLoops {
		if .Labels != nil {
			.Labels.ID = 
			++
		}
	}
	 := make([][]bool, )
	for  := range  {
		[] = make([]bool, )
	}
	for  := 0;  < len(.HyperLoops); ++ {
		 := .HyperLoops[]
		if .Labels == nil {
			continue
		}
		for  :=  + 1;  < len(.HyperLoops); ++ {
			 := .HyperLoops[]
			if .Labels == nil {
				continue
			}
			 := labelsOverlap(, )
			[.Labels.ID][.Labels.ID] = 
			[.Labels.ID][.Labels.ID] = 
		}
	}
	return 
}

func labelsOverlap(,  *loops.HyperLoop) bool {
	,  := .Labels, .Labels
	if  == nil ||  == nil || .Side != .Side ||
		.Side == layered.EastSide || .Side == layered.WestSide {
		return false
	}
	,  := .Position.X, .Position.X
	 :=  + .Size.X
	 :=  + .Size.X
	return  <=  &&  >= 
}

func ( *slotAssigner) (
	 *layered.LGraphArena,
	 *loops.Holder,
	 [][]bool,
) {
	.segments = make([]*orthogonal.HyperEdgeSegment, 0, len(.HyperLoops))
	.loopSegments = make(map[*loops.HyperLoop]*orthogonal.HyperEdgeSegment)
	for ,  := range .HyperLoops {
		 := orthogonal.NewBareSegment()
		.segments = append(.segments, )
		.loopSegments[] = 
	}
	.computeLoopActivity(, )
	for  := 0;  < len(.HyperLoops)-1; ++ {
		for  :=  + 1;  < len(.HyperLoops); ++ {
			.createDependencies(.HyperLoops[], .HyperLoops[], )
		}
	}
}

func ( *slotAssigner) ( *layered.LGraphArena,  *loops.Holder) {
	.activity = make(map[*loops.HyperLoop][]bool)
	 := len(.Node(.LNode).Ports)
	for ,  := range .HyperLoops {
		 := make([]bool, )
		.activity[] = 
		 := .Port(.LeftmostPort.LPort).ID - 1
		 := .Port(.RightmostPort.LPort).ID
		for  !=  {
			 = ( + 1) % 
			[] = true
		}
	}
}

func ( *slotAssigner) (,  *loops.HyperLoop,  [][]bool) {
	 := .countCrossings(, )
	 := .countCrossings(, )
	 := .loopSegments[]
	 := .loopSegments[]
	switch {
	case  < :
		orthogonal.AddRegularDependency(, , -)
	case  < :
		orthogonal.AddRegularDependency(, , -)
	case  != 0 || matrixLabelsOverlap(, , ):
		orthogonal.AddRegularDependency(, , 0)
		orthogonal.AddRegularDependency(, , 0)
	}
}

func ( *slotAssigner) (,  *loops.HyperLoop) int {
	 := .activity[]
	 := 0
	for ,  := range .Ports {
		// RoutingDirector assigned IDs immediately before this processor.
		if [.arena.Port(.LPort).ID] {
			++
		}
	}
	return 
}

func matrixLabelsOverlap(,  *loops.HyperLoop,  [][]bool) bool {
	if .Labels == nil || .Labels == nil {
		return false
	}
	return [.Labels.ID][.Labels.ID]
}

func ( *slotAssigner) () {
	 := make([]*orthogonal.HyperEdgeSegment, 0)
	for ,  := range .segments {
		.InWeight = len(.IncomingDependencies)
		.OutWeight = len(.OutgoingDependencies)
		if .OutWeight == 0 {
			.RoutingSlot = 0
			 = append(, )
		}
	}
	for len() != 0 {
		 := [0]
		 = [1:]
		 := .RoutingSlot + 1
		for ,  := range .IncomingDependencies {
			 := .Source
			if  > .RoutingSlot {
				.RoutingSlot = 
			}
			.OutWeight--
			if .OutWeight == 0 {
				 = append(, )
			}
		}
	}
}

func ( *slotAssigner) ( *loops.Holder) {
	for ,  := range .HyperLoops {
		 := .loopSegments[].RoutingSlot
		for  := layered.NorthSide;  <= layered.WestSide; ++ {
			if .OccupiedSides[] {
				.SetRoutingSlot(, )
			}
		}
	}
}

func ( *slotAssigner) (
	 *layered.LGraphArena,
	 *loops.Holder,
	 [][]bool,
) {
	 := make([]int, len(.Node(.LNode).Ports))
	for  := layered.NorthSide;  <= layered.WestSide; ++ {
		.shiftSide(, , , , )
	}
}

func ( *slotAssigner) (
	 *layered.LGraphArena,
	 *loops.Holder,
	 layered.PortSide,
	 []int,
	 [][]bool,
) {
	 := make([]*loops.HyperLoop, 0)
	for ,  := range .HyperLoops {
		if .OccupiedSides[] {
			 = append(, )
		}
	}
	sort.SliceStable(, func(,  int) bool {
		return [].RoutingSlot[] < [].RoutingSlot[]
	})
	,  := int(^uint(0)>>1), -int(^uint(0)>>1)-1
	for ,  := range .Node(.LNode).Ports {
		 := .Port()
		if .Side ==  {
			if .ID <  {
				 = .ID
			}
			if .ID >  {
				 = .ID
			}
		}
	}
	if  <  {
		for ,  := range  {
			.SetRoutingSlot(, )
		}
		return
	}

	 := make([]int, len())
	for  := range  {
		[] = -1
	}
	for ,  := range  {
		 := .activity[]
		 := 0
		for  := ;  <= ; ++ {
			if [] && [] >  {
				 = []
			}
		}
		if .Labels != nil {
			 := make(map[int]struct{})
			for  := range  {
				if [.Labels.ID][] {
					[[]] = struct{}{}
				}
			}
			for {
				if ,  := []; ! {
					break
				}
				++
			}
		}
		.SetRoutingSlot(, )
		for  := ;  <= ; ++ {
			if [] {
				[] =  + 1
			}
		}
		if .Labels != nil {
			[.Labels.ID] = 
		}
	}
}