// Copyright (c) 2010, 2020 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 p5edges

import (
	

	
	basegraph 
	
)

const (
	minimumVerticalDifference = 1.0
	layerSpaceFactor          = 0.4
)

// PolylineEdgeRouter routes every edge in two steps: at its source layer and
// later at its target layer. It is the exact non-spline phase-5 alternative
// from ELK Layered 0.8.1.
type PolylineEdgeRouter struct {
	createdJunctionPoints map[geometry.Point]struct{}
}

var _ layered.ConfiguredProcessor = (*PolylineEdgeRouter)(nil)

func (*PolylineEdgeRouter) (
	 *layered.LGraphArena,
	 layered.LGraphID,
) layered.ProcessorConfiguration {
	 := basegraph.Get(&.Graph().Properties, layered.GraphProperties)
	var  layered.ProcessorConfiguration
	.AddBefore(layered.PhaseNodeOrdering, layered.InvertedPortProcessor)
	if &layered.GraphPropertyNorthSouthPorts != 0 {
		.
			AddBefore(layered.PhaseNodeOrdering, layered.NorthSouthPortPreprocessor).
			AddAfter(layered.PhaseEdgeRouting, layered.NorthSouthPortPostprocessor)
	}
	if &layered.GraphPropertySelfLoops != 0 {
		.
			AddBefore(layered.PhaseCycleBreaking, layered.SelfLoopPreprocessor).
			AddBefore(layered.PhaseNodePlacement, layered.SelfLoopPortRestorer).
			AddBefore(layered.PhaseNodePlacement, layered.SelfLoopRouter).
			AddAfter(layered.PhaseEdgeRouting, layered.SelfLoopPostprocessor)
	}
	if &layered.GraphPropertyCenterLabels != 0 {
		.
			AddBefore(layered.PhaseLayering, layered.LabelDummyInserter).
			AddBefore(layered.PhaseNodePlacement, layered.LabelDummySwitcher).
			AddBefore(layered.PhaseNodePlacement, layered.LabelSideSelector).
			AddAfter(layered.PhaseEdgeRouting, layered.LabelDummyRemover)
	}
	if &layered.GraphPropertyEndLabels != 0 {
		.
			AddBefore(layered.PhaseNodePlacement, layered.LabelSideSelector).
			AddBefore(layered.PhaseNodePlacement, layered.EndLabelPreprocessor).
			AddAfter(layered.PhaseEdgeRouting, layered.EndLabelPostprocessor)
	}
	return 
}

func ( *PolylineEdgeRouter) ( *layered.LGraphArena,  layered.LGraphID) error {
	 := .Graph()
	 := basegraph.Get(&.Properties, layered.PolylineSlopedEdgeZoneWidth)
	 := basegraph.Get(&.Properties, layered.SpacingNodeNodeBetweenLayers)
	 := basegraph.Get(&.Properties, layered.SpacingEdgeEdgeBetweenLayers)
	 := math.Min(1, /)
	if .createdJunctionPoints == nil {
		.createdJunctionPoints = make(map[geometry.Point]struct{})
	}
	 := 0.0
	if len(.Layers) > 0 {
		 := .calculateWestInLayerEdgeYDifference(, .Layers[0])
		 = layerSpaceFactor *  * 
	}
	for ,  := range .Layers {
		 := .Layer()
		 := allExternalWestOrEastPorts(, .Nodes)
		if  &&  > 0 {
			 -= 
		}
		placeNodesHorizontally(, , )
		 := 0.0
		for ,  := range .Nodes {
			 := .Node()
			 := 0.0
			for ,  := range .NodeOutgoingEdges() {
				 := .Edge()
				 := absolutePortAnchor(, *.Source).Y
				 := absolutePortAnchor(, *.Target).Y
				 := .EdgeTargetNode()
				if equalLayerID(.Layer, .Node().Layer) && !.EdgeIsSelfLoop() {
					.processInLayerEdge(, , ,
						layerSpaceFactor**math.Abs(-))
					if .Port(*.Source).Side == layered.WestSide {
						,  = 0, 0
					}
				}
				 = math.Max(
					, math.Abs(-))
			}
			switch .Type {
			case layered.NormalNode, layered.LabelNode, layered.LongEdgeNode,
				layered.NorthSouthPortNode, layered.BreakingPointNode:
				.processNode(, , , )
			}
			 = math.Max(, )
		}
		if +1 < len(.Layers) {
			 := .calculateWestInLayerEdgeYDifference(, .Layers[+1])
			 = math.Max(, )
		}
		 := layerSpaceFactor *  * 
		if ! && +1 < len(.Layers) {
			 += 
		}
		 += .Size.X + 
	}
	clear(.createdJunctionPoints)
	.Size.X = 
	return nil
}

func ( *PolylineEdgeRouter) (
	 *layered.LGraphArena,
	 layered.LNodeID,
	,  float64,
) {
	 := .Node()
	 :=  + .Layer(*.Layer).Size.X
	for ,  := range .Ports {
		 := .Port()
		 := absolutePortAnchor(, )
		if .Type == layered.NorthSouthPortNode {
			 := basegraph.Get(&.Properties, layered.OriginalElement)
			if .Kind == layered.OriginLPort {
				.X = absolutePortAnchor(, .LPort).X
				.Position.X = .X
			}
		}
		 := geometry.Point{Y: .Y}
		switch .Side {
		case layered.EastSide:
			.X = 
		case layered.WestSide:
			.X = 
		default:
			continue
		}
		if math.Abs(.X-.X) <=  &&
			!.isInLayerDummy(, ) {
			continue
		}
		 := len(.OutgoingEdges)+len(.IncomingEdges) > 1
		for ,  := range .PortConnectedEdges() {
			 := .EdgeOtherPort(, )
			if math.Abs(absolutePortAnchor(, ).Y-.Y) > minimumVerticalDifference {
				.addBendPoint(, , , , )
			}
		}
	}
}

func (*PolylineEdgeRouter) (
	 *layered.LGraphArena,
	 layered.LEdgeID,
	,  float64,
) {
	 := .Edge()
	,  := *.Source, *.Target
	 := absolutePortAnchor(, ).Y
	 := ( + absolutePortAnchor(, ).Y) / 2
	 := geometry.Point{Y: }
	if .Port().Side == layered.EastSide {
		 := *.Port().Node
		.X =  + .Layer(*.Node().Layer).Size.X + 
	} else {
		.X =  - 
	}
	.BendPoints = append(.BendPoints, geometry.Point{})
	copy(.BendPoints[1:], .BendPoints[:len(.BendPoints)-1])
	.BendPoints[0] = 
}

func (*PolylineEdgeRouter) (
	 *layered.LGraphArena,
	 layered.LayerID,
) float64 {
	 := 0.0
	for ,  := range .Layer().Nodes {
		for ,  := range .NodeOutgoingEdges() {
			 := .Edge()
			if equalLayerID(.Node(.EdgeTargetNode()).Layer, &) &&
				.Port(*.Source).Side == layered.WestSide {
				 = math.Max(, math.Abs(
					absolutePortAnchor(, *.Target).Y-absolutePortAnchor(, *.Source).Y))
			}
		}
	}
	return 
}

func ( *PolylineEdgeRouter) (
	 *layered.LGraphArena,
	 layered.LEdgeID,
	 geometry.Point,
	 bool,
	 layered.LPortID,
) {
	 := .Edge()
	if (!.EdgeIsInLayer() && absolutePortAnchor(, ) == ) ||
		.EdgeIsSelfLoop() {
		return
	}
	if *.Source ==  {
		.BendPoints = append(.BendPoints, geometry.Point{})
		copy(.BendPoints[1:], .BendPoints[:len(.BendPoints)-1])
		.BendPoints[0] = 
	} else {
		.BendPoints = append(.BendPoints, )
	}
	if  {
		if ,  := .createdJunctionPoints[]; ! {
			 := basegraph.Get(&.Properties, layered.JunctionPoints)
			 = append(, )
			basegraph.Set(&.Properties, layered.JunctionPoints, )
			.createdJunctionPoints[] = struct{}{}
		}
	}
}

func (*PolylineEdgeRouter) ( *layered.LGraphArena,  layered.LNodeID) bool {
	if .Node().Type != layered.LongEdgeNode {
		return false
	}
	for ,  := range .NodeConnectedEdges() {
		if .EdgeIsInLayer() {
			return true
		}
	}
	return false
}

func absolutePortAnchor( *layered.LGraphArena,  layered.LPortID) geometry.Point {
	 := .Port()
	return .Node(*.Node).Position.Add(.Position).Add(.Anchor)
}

func equalLayerID(,  *layered.LayerID) bool {
	if  == nil ||  == nil {
		return  == nil &&  == nil
	}
	return * == *
}