// Copyright (c) 2012, 2017 Kiel University and others.
//
// 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 intermediate

import (
	

	basegraph 
	
)

// SelectLabelSides annotates edge labels and label dummies with the side on
// which they must be placed.
func ( *layered.LGraphArena,  layered.LGraphID) {
	 := basegraph.Get(&.Graph().Properties, layered.EdgeLabelSideSelectionOption)
	switch  {
	case layered.EdgeLabelSideAlwaysUp:
		setAllLabelSides(, , layered.LabelSideAbove)
	case layered.EdgeLabelSideAlwaysDown:
		setAllLabelSides(, , layered.LabelSideBelow)
	case layered.EdgeLabelSideDirectionUp:
		setDirectionLabelSides(, , layered.LabelSideAbove)
	case layered.EdgeLabelSideDirectionDown:
		setDirectionLabelSides(, , layered.LabelSideBelow)
	case layered.EdgeLabelSideSmartUp:
		selectSmartLabelSides(, , layered.LabelSideAbove)
	case layered.EdgeLabelSideSmartDown:
		selectSmartLabelSides(, , layered.LabelSideBelow)
	}
}

func setAllLabelSides( *layered.LGraphArena,  layered.LGraphID,  layered.LabelSide) {
	for ,  := range .Graph().Layers {
		for ,  := range .Layer().Nodes {
			if .Node().Type == layered.LabelNode {
				applyLabelDummySide(, , , )
			}
			for ,  := range .NodeOutgoingEdges() {
				applyEdgeLabelSide(, , )
			}
		}
	}
}

func setDirectionLabelSides( *layered.LGraphArena,  layered.LGraphID,  layered.LabelSide) {
	for ,  := range .Graph().Layers {
		for ,  := range .Layer().Nodes {
			if .Node().Type == layered.LabelNode {
				 := 
				if !labelDummyPointsRight(, ) {
					 = oppositeLabelSide()
				}
				applyLabelDummySide(, , , )
			}
			for ,  := range .NodeOutgoingEdges() {
				 := 
				if basegraph.Get(&.Edge().Properties, layered.Reversed) {
					 = oppositeLabelSide()
				}
				applyEdgeLabelSide(, , )
			}
		}
	}
}

func oppositeLabelSide( layered.LabelSide) layered.LabelSide {
	if  == layered.LabelSideAbove {
		return layered.LabelSideBelow
	}
	if  == layered.LabelSideBelow {
		return layered.LabelSideAbove
	}
	return 
}

func selectSmartLabelSides( *layered.LGraphArena,  layered.LGraphID,  layered.LabelSide) {
	 := make([]layered.LNodeID, 0)
	for ,  := range .Graph().Layers {
		 := true
		 := 0
		for ,  := range .Layer().Nodes {
			switch .Node().Type {
			case layered.LabelNode:
				++
				 = append(, )
			case layered.LongEdgeNode:
				 = append(, )
			case layered.NormalNode:
				smartRegularNodeLabels(, , )
				if len() != 0 {
					smartDummyRun(, , , , , false, )
					 = [:0]
				}
				,  = false, 0
			default:
				if len() != 0 {
					smartDummyRun(, , , , , false, )
					 = [:0]
				}
				,  = false, 0
			}
		}
		if len() != 0 {
			smartDummyRun(, , , , , true, )
			 = [:0]
		}
	}
}

func smartDummyRun(
	 *layered.LGraphArena,
	 layered.LGraphID,
	 []layered.LNodeID,
	 int,
	,  bool,
	 layered.LabelSide,
) {
	switch {
	case  && (! || len() > 1) &&  == 1 && .Node([0]).Type == layered.LabelNode:
		applyLabelDummySide(, , [0], layered.LabelSideAbove)
	case  && (! || len() > 1) &&  == 1 && .Node([len()-1]).Type == layered.LabelNode:
		applyLabelDummySide(, , [len()-1], layered.LabelSideBelow)
	case len() == 2:
		applyLabelDummySide(, , [0], layered.LabelSideAbove)
		applyLabelDummySide(, , [1], layered.LabelSideBelow)
	default:
		applyDummyRunWithSimpleLoops(, , , )
	}
}

func applyDummyRunWithSimpleLoops(
	 *layered.LGraphArena,
	 layered.LGraphID,
	 []layered.LNodeID,
	 layered.LabelSide,
) {
	 := make([]layered.LNodeID, 0, len())
	var ,  layered.LNodeID
	,  := false, false
	for ,  := range  {
		,  := longEdgeEndNode(, , true)
		,  := longEdgeEndNode(, , false)
		if  !=  ||  !=  ||
			( &&  != ) || ( &&  != ) {
			applyLabelDummyRun(, , , )
			 = [:0]
			,  = , 
			,  = , 
		}
		 = append(, )
	}
	applyLabelDummyRun(, , , )
}

func longEdgeEndNode( *layered.LGraphArena,  layered.LNodeID,  bool) (layered.LNodeID, bool) {
	 := &.Node().Properties
	var  layered.LPortID
	var  bool
	if  {
		,  = basegraph.TryGet(, layered.LongEdgeSource)
	} else {
		,  = basegraph.TryGet(, layered.LongEdgeTarget)
	}
	if ! || .Port().Node == nil {
		return 0, false
	}
	return *.Port().Node, true
}

func applyLabelDummyRun(
	 *layered.LGraphArena,
	 layered.LGraphID,
	 []layered.LNodeID,
	 layered.LabelSide,
) {
	if len() == 0 {
		return
	}
	if len() == 2 {
		applyLabelDummySide(, , [0], layered.LabelSideAbove)
		applyLabelDummySide(, , [1], layered.LabelSideBelow)
		return
	}
	for ,  := range  {
		applyLabelDummySide(, , , )
	}
}

func smartRegularNodeLabels( *layered.LGraphArena,  layered.LNodeID,  layered.LabelSide) {
	 := make([][]layered.LLabelID, 0, len(.Node().Ports))
	 := layered.UndefinedSide
	for ,  := range .Node().Ports {
		 := .Port()
		if .Side !=  {
			if len() != 0 {
				applyPortEndLabelQueue(, , , )
			}
			 = [:0]
			 = .Side
		}
		,  := gatherPortEndLabels(, )
		if  {
			 = append(, )
		}
	}
	if len() != 0 {
		applyPortEndLabelQueue(, , , )
	}
}

func applyPortEndLabelQueue(
	 *layered.LGraphArena,
	 [][]layered.LLabelID,
	 layered.PortSide,
	 layered.LabelSide,
) {
	if len() == 2 {
		if  == layered.NorthSide ||  == layered.EastSide {
			applyLabelListSide(, [0], layered.LabelSideAbove)
			applyLabelListSide(, [1], layered.LabelSideBelow)
		} else {
			applyLabelListSide(, [0], layered.LabelSideBelow)
			applyLabelListSide(, [1], layered.LabelSideAbove)
		}
		return
	}
	for ,  := range  {
		applyLabelListSide(, , )
	}
}

func gatherPortEndLabels( *layered.LGraphArena,  layered.LPortID) ([]layered.LLabelID, bool) {
	 := make([]layered.LLabelID, 0)
	 := gatherPortLabelsDirect(, , &)
	if ,  := basegraph.TryGet(&.Port().Properties, layered.PortDummy);  {
		for ,  := range .Node().Ports {
			,  := basegraph.TryGet(&.Port().Properties, layered.OriginalElement)
			if  && ((.Kind == layered.OriginLPort && .LPort == ) ||
				(.Kind == layered.OriginPort && uint32(.Port) == uint32())) {
				 = math.Max(, gatherPortLabelsDirect(, , &))
			}
		}
	}
	if len() != 0 {
		basegraph.Set(&.Port().Properties, layered.MaximumEdgeThickness, )
	}
	return ,  != -1
}

func gatherPortLabelsDirect( *layered.LGraphArena,  layered.LPortID,  *[]layered.LLabelID) float64 {
	 := -1.0
	for ,  := range .PortConnectedEdges() {
		 := .Edge()
		 = math.Max(, basegraph.Get(&.Properties, layered.EdgeThickness))
		 := layered.EdgeLabelPlacementHead
		if .Source != nil && *.Source ==  {
			 = layered.EdgeLabelPlacementTail
		}
		for ,  := range .Labels {
			if basegraph.Get(&.Label().Properties, layered.EdgeLabelsPlacement) !=  {
				continue
			}
			* = append(*, )
			if !basegraph.Has(&.Label().Properties, layered.EndLabelEdge) {
				basegraph.Set(&.Label().Properties, layered.EndLabelEdge, )
			}
		}
	}
	return 
}

func applyLabelDummySide(
	 *layered.LGraphArena,
	 layered.LGraphID,
	 layered.LNodeID,
	 layered.LabelSide,
) {
	 := .Node()
	if .Type != layered.LabelNode {
		return
	}
	 := 
	 := true
	for ,  := range basegraph.Get(&.Properties, layered.RepresentedLabels) {
		if !basegraph.Get(&.Label().Properties, layered.EdgeLabelsInline) {
			 = false
			break
		}
	}
	if  {
		 = layered.LabelSideInline
	}
	basegraph.Set(&.Properties, layered.InternalLabelSide, )
	if  == layered.LabelSideBelow {
		return
	}
	 := basegraph.Get(&.Properties, layered.OriginalElement)
	if .Kind != layered.OriginLEdge {
		panic("layered: label dummy origin is not an LEdge")
	}
	 := basegraph.Get(&.Edge(.LEdge).Properties, layered.EdgeThickness)
	 := 0.0
	if  == layered.LabelSideAbove {
		 = .Size.Y - math.Ceil(/2)
	} else {
		 := basegraph.Get(
			&.Graph().Properties, layered.SpacingEdgeLabel)
		 = math.Ceil(.Size.Y--) / 2
		.Size.Y -= 
		.Size.Y -= 
	}
	for ,  := range .Ports {
		.Port().Position.Y = 
	}
}

func applyEdgeLabelSide( *layered.LGraphArena,  layered.LEdgeID,  layered.LabelSide) {
	applyLabelListSide(, .Edge().Labels, )
}

func applyLabelListSide( *layered.LGraphArena,  []layered.LLabelID,  layered.LabelSide) {
	for ,  := range  {
		basegraph.Set(&.Label().Properties, layered.InternalLabelSide, )
	}
}

func labelDummyPointsRight( *layered.LGraphArena,  layered.LNodeID) bool {
	,  := .NodeIncomingEdges(), .NodeOutgoingEdges()
	if len() == 0 || len() == 0 {
		panic("layered: label dummy has incomplete edge chain")
	}
	return !basegraph.Get(&.Edge([0]).Properties, layered.Reversed) ||
		!basegraph.Get(&.Edge([0]).Properties, layered.Reversed)
}