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

package intermediate

import (
	

	
	basegraph 
	
	
)

// RouteHierarchicalPorts restores north/south external-port dummies, routes
// their temporary connections, and fixes every external dummy to the graph
// border. It is ELK's HierarchicalPortOrthogonalEdgeRouter.
func ( *layered.LGraphArena,  layered.LGraphID) error {
	 := restoreNorthSouthHierarchyDummies(, )
	setNorthSouthHierarchyCoordinates(, , )
	routeNorthSouthHierarchyEdges(, , )
	removeTemporaryNorthSouthHierarchyDummies(, )
	fixHierarchyDummyCoordinates(, )
	correctHierarchySlantedSegments(, )
	return nil
}

func restoreNorthSouthHierarchyDummies(
	 *layered.LGraphArena,
	 layered.LGraphID,
) []layered.LNodeID {
	,  := basegraph.TryGet(&.Graph().Properties,
		layered.ExternalPortReplacedDummies)
	if ! || len() == 0 {
		return nil
	}
	 := append([]layered.LNodeID(nil), ...)
	for ,  := range  {
		 := .Node().Ports[0]
		switch basegraph.Get(&.Node().Properties, layered.ExternalPortSide) {
		case layered.NorthSide:
			.SetPortSide(, layered.SouthSide)
		case layered.SouthSide:
			.SetPortSide(, layered.NorthSide)
		}
	}
	for ,  := range append([]layered.LayerID(nil), .Graph().Layers...) {
		for ,  := range append([]layered.LNodeID(nil), .Layer().Nodes...) {
			if .Node().Type != layered.ExternalPortNode {
				continue
			}
			,  := basegraph.TryGet(&.Node().Properties,
				layered.ExternalPortReplacedDummy)
			if  {
				connectHierarchyRepresentative(, , )
			}
		}
	}
	if  := .Graph().Layers; len() > 0 {
		 := [len()-1]
		for ,  := range  {
			.SetNodeLayer(, &)
		}
	}
	return 
}

func connectHierarchyRepresentative(
	 *layered.LGraphArena,
	,  layered.LNodeID,
) {
	 := .AddPort()
	.SetPortNode(, &)
	.SetPortSide(, basegraph.Get(&.Node().Properties,
		layered.ExternalPortSide))
	 := .Node().Ports[0]
	 := .AddEdge()
	.SetEdgeSource(, &)
	.SetEdgeTarget(, &)
}

func setNorthSouthHierarchyCoordinates(
	 *layered.LGraphArena,
	 layered.LGraphID,
	 []layered.LNodeID,
) {
	 := basegraph.Get(&.Graph().Properties, layered.PortConstraintsOption)
	 := .Graph()
	 := .Size
	 := .Size.X + .Padding.Left + .Padding.Right
	 := -.Padding.Top - .Offset.Y
	 := .Size.Y + .Padding.Top + .Padding.Bottom - .Offset.Y
	,  := make([]layered.LNodeID, 0), make([]layered.LNodeID, 0)
	for ,  := range  {
		switch  {
		case layered.PortConstraintsFree, layered.PortConstraintsFixedSide,
			layered.PortConstraintsFixedOrder, layered.PortConstraintsUndefined:
			calculateNorthSouthHierarchyPosition(, )
		case layered.PortConstraintsFixedRatio:
			 := basegraph.Get(&.Node().Properties, layered.PortAnchor)
			 := basegraph.Get(&.Node().Properties, layered.PortRatioOrPosition)
			.Node().Position.X = * - .X
			.NodeBorderToContentCoordinates(, true, false)
		case layered.PortConstraintsFixedPosition:
			 := basegraph.Get(&.Node().Properties, layered.PortAnchor)
			 := basegraph.Get(&.Node().Properties, layered.PortRatioOrPosition)
			.Node().Position.X =  - .X
			.NodeBorderToContentCoordinates(, true, false)
			 := .Node().Position.X + .Node().Size.X/2
			if  > .X {
				.Graph().Size.X = 
			}
		}
		switch basegraph.Get(&.Node().Properties, layered.ExternalPortSide) {
		case layered.NorthSide:
			.Node().Position.Y = 
			 = append(, )
		case layered.SouthSide:
			.Node().Position.Y = 
			 = append(, )
		}
	}
	switch  {
	case layered.PortConstraintsFree, layered.PortConstraintsFixedSide,
		layered.PortConstraintsUndefined:
		ensureUniqueHierarchyPositions(, , , false)
		ensureUniqueHierarchyPositions(, , , false)
	case layered.PortConstraintsFixedOrder:
		ensureUniqueHierarchyPositions(, , , true)
		ensureUniqueHierarchyPositions(, , , true)
	}
}

func calculateNorthSouthHierarchyPosition( *layered.LGraphArena,  layered.LNodeID) {
	 := .Node().Ports[0]
	 := .PortConnectedEdges()
	if len() == 0 {
		.Node().Position.X = 0
		return
	}
	 := 0.0
	for ,  := range  {
		 := .EdgeOtherPort(, )
		 := .Port()
		 += .Node(*.Node).Position.X + .Position.X + .Anchor.X
	}
	 := basegraph.Get(&.Node().Properties, layered.PortAnchor)
	.Node().Position.X = /float64(len()) - .X
}

func ensureUniqueHierarchyPositions(
	 *layered.LGraphArena,
	 layered.LGraphID,
	 []layered.LNodeID,
	 bool,
) {
	if len() == 0 {
		return
	}
	 = append([]layered.LNodeID(nil), ...)
	sort.SliceStable(, func(,  int) bool {
		if  {
			return basegraph.Get(&.Node([]).Properties, layered.PortRatioOrPosition) <
				basegraph.Get(&.Node([]).Properties, layered.PortRatioOrPosition)
		}
		return .Node([]).Position.X < .Node([]).Position.X
	})
	 := basegraph.Get(&.Graph().Properties, layered.SpacingPortPort)
	 := .Node([0])
	 := .Position.X + .Size.X + .Margin.Right + 
	for ,  := range [1:] {
		 := .Node()
		 := .Position.X - .Margin.Left - 
		if  < 0 {
			.Position.X -= 
		}
		if  := .Position.X + .Size.X;  > .Graph().Size.X {
			.Graph().Size.X = 
		}
		 = .Position.X + .Size.X + .Margin.Right + 
	}
}

func routeNorthSouthHierarchyEdges(
	 *layered.LGraphArena,
	 layered.LGraphID,
	 []layered.LNodeID,
) {
	,  := make([]layered.LNodeID, 0), make([]layered.LNodeID, 0)
	,  := make([]layered.LNodeID, 0), make([]layered.LNodeID, 0)
	for ,  := range  {
		switch basegraph.Get(&.Node().Properties, layered.ExternalPortSide) {
		case layered.NorthSide:
			appendUniqueNode(&, )
			for ,  := range .NodeIncomingEdges() {
				appendUniqueNode(&, .EdgeSourceNode())
			}
		case layered.SouthSide:
			appendUniqueNode(&, )
			for ,  := range .NodeIncomingEdges() {
				appendUniqueNode(&, .EdgeSourceNode())
			}
		}
	}
	 := basegraph.Get(&.Graph().Properties, layered.SpacingNodeNode)
	 := basegraph.Get(&.Graph().Properties, layered.SpacingEdgeEdge)
	if len() > 0 {
		 := orthogonal.NewGenerator(orthogonal.SouthToNorth, )
		 := - - .Graph().Offset.Y
		 := .RouteEdges(, , , 0, , )
		if  > 0 {
			 :=  + float64(-1)*
			.Graph().Offset.Y += 
			.Graph().Size.Y += 
		}
	}
	if len() > 0 {
		 := orthogonal.NewGenerator(orthogonal.NorthToSouth, )
		 := .Graph().Size.Y +  - .Graph().Offset.Y
		 := .RouteEdges(, , , 0, , )
		if  > 0 {
			.Graph().Size.Y +=  + float64(-1)*
		}
	}
}

func appendUniqueNode( *[]layered.LNodeID,  layered.LNodeID) {
	for ,  := range * {
		if  ==  {
			return
		}
	}
	* = append(*, )
}

func removeTemporaryNorthSouthHierarchyDummies(
	 *layered.LGraphArena,
	 layered.LGraphID,
) {
	 := make([]layered.LNodeID, 0)
	for ,  := range append([]layered.LayerID(nil), .Graph().Layers...) {
		for ,  := range append([]layered.LNodeID(nil), .Layer().Nodes...) {
			,  := basegraph.TryGet(&.Node().Properties,
				layered.ExternalPortReplacedDummy)
			if .Node().Type != layered.ExternalPortNode || ! {
				continue
			}
			var , ,  *layered.LPortID
			for ,  := range .Node().Ports {
				 := 
				switch .Port().Side {
				case layered.WestSide:
					 = &
				case layered.EastSide:
					 = &
				default:
					 = &
				}
			}
			if  == nil ||  == nil ||  == nil ||
				len(.Port(*).OutgoingEdges) == 0 {
				continue
			}
			 := .Port(*).OutgoingEdges[0]
			 := .Edge().BendPoints.Clone()
			 := .Node().Position.Add(.Port(*).Position)
			 = append(geometry.Chain{}, ...)
			 := .Edge().BendPoints.Reverse()
			 = append(,
				.Node().Position.Add(.Port(*).Position))
			 := .Node().Ports[0]
			for ,  := range append([]layered.LEdgeID(nil), .Port(*).IncomingEdges...) {
				.SetEdgeTarget(, &)
				.Edge().BendPoints = append(.Edge().BendPoints, ...)
			}
			for ,  := range append([]layered.LEdgeID(nil), .Port(*).OutgoingEdges...) {
				.SetEdgeSource(, &)
				 := append(geometry.Chain(nil), ...)
				.Edge().BendPoints = append(, .Edge().BendPoints...)
			}
			.SetEdgeSource(, nil)
			.SetEdgeTarget(, nil)
			 = append(, )
		}
	}
	for ,  := range  {
		.SetNodeLayer(, nil)
	}
}

func fixHierarchyDummyCoordinates( *layered.LGraphArena,  layered.LGraphID) {
	 := .Graph().Layers
	if len() == 0 {
		return
	}
	 := basegraph.Get(&.Graph().Properties, layered.PortConstraintsOption)
	fixHierarchyDummyLayerCoordinates(, , [0], )
	fixHierarchyDummyLayerCoordinates(, , [len()-1], )
}

func fixHierarchyDummyLayerCoordinates(
	 *layered.LGraphArena,
	 layered.LGraphID,
	 layered.LayerID,
	 layered.PortConstraints,
) {
	 := .Graph()
	 := .GraphActualSize().Y
	 := 
	 := append([]layered.LNodeID(nil), .Layer().Nodes...)
	for ,  := range  {
		if .Node().Type != layered.ExternalPortNode {
			continue
		}
		 := basegraph.Get(&.Node().Properties, layered.ExternalPortSide)
		 := basegraph.Get(&.Node().Properties, layered.ExternalPortSize)
		switch  {
		case layered.EastSide:
			.Node().Position.X = .Size.X + .Padding.Right - .Offset.X
		case layered.WestSide:
			.Node().Position.X = -.Offset.X - .Padding.Left
		}
		 := 0.0
		if  == layered.EastSide ||  == layered.WestSide {
			 := basegraph.Get(&.Node().Properties, layered.PortAnchor)
			switch  {
			case layered.PortConstraintsFixedRatio:
				 := basegraph.Get(&.Node().Properties, layered.PortRatioOrPosition)
				.Node().Position.Y = * - .Y
				 = .Node().Position.Y + .Y
				.NodeBorderToContentCoordinates(, false, true)
			case layered.PortConstraintsFixedPosition:
				 := basegraph.Get(&.Node().Properties, layered.PortRatioOrPosition)
				.Node().Position.Y =  - .Y
				 = .Node().Position.Y + .Y
				.NodeBorderToContentCoordinates(, false, true)
			}
		}
		if  >  {
			 = 
		}
	}
	.Graph().Size.Y +=  - 
	 = .Graph()
	for ,  := range  {
		if .Node().Type != layered.ExternalPortNode {
			continue
		}
		switch basegraph.Get(&.Node().Properties, layered.ExternalPortSide) {
		case layered.NorthSide:
			.Node().Position.Y = -.Offset.Y - .Padding.Top
		case layered.SouthSide:
			.Node().Position.Y = .Size.Y + .Padding.Bottom - .Offset.Y
		}
	}
}

func correctHierarchySlantedSegments( *layered.LGraphArena,  layered.LGraphID) {
	 := .Graph().Layers
	if len() == 0 {
		return
	}
	correctHierarchySlantedLayer(, [0])
	correctHierarchySlantedLayer(, [len()-1])
}

func correctHierarchySlantedLayer( *layered.LGraphArena,  layered.LayerID) {
	for ,  := range append([]layered.LNodeID(nil), .Layer().Nodes...) {
		if .Node().Type != layered.ExternalPortNode {
			continue
		}
		 := basegraph.Get(&.Node().Properties, layered.ExternalPortSide)
		if  != layered.EastSide &&  != layered.WestSide {
			continue
		}
		for ,  := range .NodeConnectedEdges() {
			 := .Edge()
			if len(.BendPoints) == 0 {
				continue
			}
			if .Source != nil && .Port(*.Source).Node != nil &&
				*.Port(*.Source).Node ==  {
				.BendPoints[0].Y = hierarchyPortAbsoluteAnchor(, *.Source).Y
			}
			if .Target != nil && .Port(*.Target).Node != nil &&
				*.Port(*.Target).Node ==  {
				.BendPoints[len(.BendPoints)-1].Y =
					hierarchyPortAbsoluteAnchor(, *.Target).Y
			}
		}
	}
}

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