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

package routing

import (
	
	

	basegraph 
	
	
)

// PlaceLabels determines each aggregate self-loop label's side, alignment,
// and first coordinate. JSON ELK graphs cannot supply Java's ILabelManager,
// so the optional management branch is intentionally inactive, as in elkjs.
func ( *layered.LGraphArena,  *loops.Holder) error {
	var ,  []*loops.HyperLoop
	 := basegraph.Get(&.Node(.LNode).Properties, layered.SelfLoopOrdering)

	for ,  := range .HyperLoops {
		if .Labels == nil {
			continue
		}
		switch .Type {
		case loops.OneSide:
			 := firstOccupiedSide()
			if  == layered.SelfLoopOrderingSequenced &&  == layered.NorthSide {
				 = append(, )
			} else if  == layered.SelfLoopOrderingSequenced &&  == layered.SouthSide {
				 = append(, )
			} else {
				assignOneSidedSimple(, , )
			}
		case loops.TwoSidesCorner:
			assignTwoSidesCorner(, )
		case loops.TwoSidesOpposing, loops.ThreeSides:
			assignOpposingAndThree(, )
		case loops.FourSides:
			assignFourSides(, )
		default:
			return fmt.Errorf("unsupported self-loop label placement type")
		}
	}
	if len() != 0 {
		assignOneSidedSequenced(, , layered.NorthSide)
	}
	if len() != 0 {
		assignOneSidedSequenced(, , layered.SouthSide)
	}
	for ,  := range .HyperLoops {
		if .Labels != nil {
			computeLabelCoordinates(, )
		}
	}
	return nil
}

func assignOneSidedSimple( *layered.LGraphArena,  *loops.HyperLoop,  layered.PortSide) {
	clearInlineLabels(, )
	switch  {
	case layered.EastSide, layered.WestSide:
		 := .LeftmostPort
		if .Port(.RightmostPort.LPort).Position.Y < .Port(.LPort).Position.Y {
			 = .RightmostPort
		}
		assignLabel(, , loops.LabelTop, )
	case layered.NorthSide, layered.SouthSide:
		assignLabel(, , loops.LabelCenter, nil)
	}
}

func assignOneSidedSequenced(
	 *layered.LGraphArena,
	 []*loops.HyperLoop,
	 layered.PortSide,
) {
	for ,  := range .Node([0].Holder.LNode).Ports {
		.Port().ID = 
	}
	sort.SliceStable(, func(,  int) bool {
		 := .Port([].LeftmostPort.LPort).ID
		 := .Port([].LeftmostPort.LPort).ID
		if  == layered.NorthSide {
			return  < 
		}
		return  > 
	})
	,  := 0, len()-1
	for  <  {
		,  := [], []
		,  := .RightmostPort, .LeftmostPort
		if  == layered.SouthSide {
			,  = .LeftmostPort, .RightmostPort
		}
		assignLabel(, , loops.LabelRight, )
		assignLabel(, , loops.LabelLeft, )
		++
		--
	}
	if  ==  {
		assignLabel([], , loops.LabelCenter, nil)
	}
}

func assignTwoSidesCorner( *layered.LGraphArena,  *loops.HyperLoop) {
	clearInlineLabels(, )
	 := .Port(.LeftmostPort.LPort).Side
	 := .Port(.RightmostPort.LPort).Side
	switch {
	case  == layered.NorthSide:
		assignLabel(, layered.NorthSide, loops.LabelLeft, .LeftmostPort)
	case  == layered.NorthSide:
		assignLabel(, layered.NorthSide, loops.LabelRight, .RightmostPort)
	case  == layered.SouthSide:
		assignLabel(, layered.SouthSide, loops.LabelRight, .LeftmostPort)
	case  == layered.SouthSide:
		assignLabel(, layered.SouthSide, loops.LabelLeft, .RightmostPort)
	}
}

func assignOpposingAndThree( *layered.LGraphArena,  *loops.HyperLoop) {
	 := .OccupiedSides
	 := hasInlineLabels(, )
	switch {
	case ![layered.NorthSide]:
		assignLabel(, layered.SouthSide, loops.LabelCenter, nil)
	case ![layered.SouthSide]:
		assignLabel(, layered.NorthSide, loops.LabelCenter, nil)
	case ![layered.WestSide]:
		if  {
			assignLabel(, layered.EastSide, loops.LabelCenter, nil)
		} else {
			assignLabel(, layered.NorthSide, loops.LabelLeft, .LeftmostPort)
		}
	case ![layered.EastSide]:
		if  {
			assignLabel(, layered.WestSide, loops.LabelCenter, nil)
		} else {
			assignLabel(, layered.NorthSide, loops.LabelRight, .RightmostPort)
		}
	}
}

func assignFourSides( *layered.LGraphArena,  *loops.HyperLoop) {
	clearInlineLabels(, )
	 := .Port(.LeftmostPort.LPort).Side
	// ELK 0.8.1 reads leftmost twice here. Keep the typo for elkjs parity.
	 := .Port(.LeftmostPort.LPort).Side
	if  == layered.NorthSide ||  == layered.NorthSide {
		assignLabel(, layered.SouthSide, loops.LabelCenter, nil)
	} else {
		assignLabel(, layered.NorthSide, loops.LabelCenter, nil)
	}
}

func hasInlineLabels( *layered.LGraphArena,  *loops.HyperLoop) bool {
	if .Labels == nil {
		return false
	}
	for ,  := range .Labels.LLabels {
		if basegraph.Get(&.Label().Properties, layered.EdgeLabelsInline) {
			return true
		}
	}
	return false
}

func clearInlineLabels( *layered.LGraphArena,  *loops.HyperLoop) {
	if .Labels == nil {
		return
	}
	for ,  := range .Labels.LLabels {
		basegraph.Unset(&.Label().Properties, layered.EdgeLabelsInline)
	}
}

func assignLabel(
	 *loops.HyperLoop,
	 layered.PortSide,
	 loops.LabelAlignment,
	 *loops.Port,
) {
	.Labels.Side = 
	.Labels.Alignment = 
	.Labels.AlignmentReference = 
}

func computeLabelCoordinates( *layered.LGraphArena,  *loops.HyperLoop) {
	 := .Labels
	switch .Alignment {
	case loops.LabelCenter:
		.Position.X = (.Node(.Holder.LNode).Size.X - .Size.X) / 2
	case loops.LabelLeft:
		 := .Port(.AlignmentReference.LPort)
		.Position.X = .Position.X + .Anchor.X
	case loops.LabelRight:
		 := .Port(.AlignmentReference.LPort)
		.Position.X = .Position.X + .Anchor.X - .Size.X
	case loops.LabelTop:
		 := .Port(.AlignmentReference.LPort)
		.Position.Y = .Position.Y + .Anchor.Y
	}
}

func firstOccupiedSide( *loops.HyperLoop) layered.PortSide {
	for  := layered.NorthSide;  <= layered.WestSide; ++ {
		if .OccupiedSides[] {
			return 
		}
	}
	return layered.UndefinedSide
}