// Copyright (c) 2011, 2015 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 
	
)

// PostprocessNorthSouthPorts ports ELK 0.8.1's
// NorthSouthPortPostprocessor. It removes the temporary in-layer dummies,
// reconnects their edges to the original ports, and adds the vertical routing
// segments (or records the dummy y coordinate for spline routing).
func ( *layered.LGraphArena,  layered.LGraphID) {
	 := basegraph.Get(&.Graph().Properties, layered.EdgeRoutingOption)
	for ,  := range .Graph().Layers {
		 := append([]layered.LNodeID(nil), .Layer().Nodes...)
		for ,  := range  {
			 := .Node()
			if .Type != layered.NorthSouthPortNode {
				continue
			}

			if  == layered.EdgeRoutingSplines {
				for ,  := range .Ports {
					if len(.Port().IncomingEdges) != 0 {
						processSplineNorthSouthInput(, )
					}
					if len(.Port().OutgoingEdges) != 0 {
						processSplineNorthSouthOutput(, )
					}
				}
			} else if ,  := basegraph.TryGet(&.Properties, layered.OriginalElement);  && .Kind == layered.OriginLEdge {
				processNorthSouthSelfLoop(, , .LEdge)
			} else {
				 := northSouthDummyPortsShareOrigin(, )
				for ,  := range append([]layered.LPortID(nil), .Ports...) {
					if len(.Port().IncomingEdges) != 0 {
						processNorthSouthInput(, , )
					}
					if len(.Port().OutgoingEdges) != 0 {
						processNorthSouthOutput(, , )
					}
				}
			}
			.SetNodeLayer(, nil)
		}
	}
}

func northSouthDummyPortsShareOrigin( *layered.LGraphArena,  layered.LNodeID) bool {
	 := .Node().Ports
	if len() < 2 {
		return false
	}
	,  := northSouthOriginPort(, [0])
	if ! {
		return false
	}
	for ,  := range [1:] {
		,  := northSouthOriginPort(, )
		if ! ||  !=  {
			return false
		}
		 = 
	}
	return true
}

func northSouthOriginPort( *layered.LGraphArena,  layered.LPortID) (layered.LPortID, bool) {
	,  := basegraph.TryGet(&.Port().Properties, layered.OriginalElement)
	return .LPort,  && .Kind == layered.OriginLPort
}

func mustNorthSouthOriginPort( *layered.LGraphArena,  layered.LPortID) layered.LPortID {
	,  := northSouthOriginPort(, )
	if ! {
		panic("layered: north/south dummy port has no LPort origin")
	}
	return 
}

func processNorthSouthInput( *layered.LGraphArena,  layered.LPortID,  bool) {
	 := .Port()
	 := mustNorthSouthOriginPort(, )
	 := northSouthAbsoluteAnchor(, ).X
	 := .Node(*.Node).Position.Y
	for ,  := range append([]layered.LEdgeID(nil), .IncomingEdges...) {
		.SetEdgeTarget(, &)
		.Edge().BendPoints = append(.Edge().BendPoints, geometry.Point{X: , Y: })
		if  {
			appendNorthSouthJunction(, , geometry.Point{X: , Y: })
		}
	}
}

func processNorthSouthOutput( *layered.LGraphArena,  layered.LPortID,  bool) {
	 := .Port()
	 := mustNorthSouthOriginPort(, )
	 := northSouthAbsoluteAnchor(, ).X
	 := .Node(*.Node).Position.Y
	for ,  := range append([]layered.LEdgeID(nil), .OutgoingEdges...) {
		.SetEdgeSource(, &)
		 := .Edge().BendPoints
		 = append(, geometry.Point{})
		copy([1:], [:len()-1])
		[0] = geometry.Point{X: , Y: }
		.Edge().BendPoints = 
		if  {
			appendNorthSouthJunction(, , geometry.Point{X: , Y: })
		}
	}
}

func appendNorthSouthJunction( *layered.LGraphArena,  layered.LEdgeID,  geometry.Point) {
	,  := basegraph.TryGet(&.Edge().Properties, layered.JunctionPoints)
	 = append(, )
	basegraph.Set(&.Edge().Properties, layered.JunctionPoints, )
}

func processNorthSouthSelfLoop(
	 *layered.LGraphArena,
	 layered.LNodeID,
	 layered.LEdgeID,
) {
	 := .NodePortsOnSide(, layered.WestSide)
	 := .NodePortsOnSide(, layered.EastSide)
	if len() == 0 || len() == 0 {
		panic("layered: north/south self-loop dummy is missing west/east ports")
	}
	,  := [0], [0]
	 := mustNorthSouthOriginPort(, )
	 := mustNorthSouthOriginPort(, )
	.SetEdgeSource(, &)
	.SetEdgeTarget(, &)
	 := .Node().Position
	.Edge().BendPoints = append(.Edge().BendPoints,
		geometry.Point{X: northSouthAbsoluteAnchor(, ).X, Y: .Y},
		geometry.Point{X: northSouthAbsoluteAnchor(, ).X, Y: .Y})
}

func processSplineNorthSouthInput( *layered.LGraphArena,  layered.LPortID) {
	 := .Port()
	 := mustNorthSouthOriginPort(, )
	basegraph.Set(&.Port().Properties, layered.SplineNorthSouthPortY,
		.Node(*.Node).Position.Y)
	for ,  := range append([]layered.LEdgeID(nil), .IncomingEdges...) {
		.SetEdgeTarget(, &)
	}
}

func processSplineNorthSouthOutput( *layered.LGraphArena,  layered.LPortID) {
	 := .Port()
	 := mustNorthSouthOriginPort(, )
	basegraph.Set(&.Port().Properties, layered.SplineNorthSouthPortY,
		.Node(*.Node).Position.Y)
	for ,  := range append([]layered.LEdgeID(nil), .OutgoingEdges...) {
		.SetEdgeSource(, &)
	}
}

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