// Copyright (c) 2010, 2017 Kiel University and others.
// Copyright (c) 2026 The elk-go Authors.
//
// 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 
	
)

// ReverseConstraintEdges enforces ELK's outer-layer constraints and the
// fixed-side feedback-node rule before cycle breaking.
func ( *layered.LGraphArena,  layered.LGraphID) {
	 := make([]layered.LNodeID, 0, len(.Graph().LayerlessNodes))
	for ,  := range .Graph().LayerlessNodes {
		 := basegraph.Get(&.Node().Properties, layered.LayerConstraintOption)
		,  := constraintPortType()
		if ! {
			 = append(, )
			continue
		}
		// ELK 0.8.1 writes OUTGOING_ONLY for both branches here.
		basegraph.Set(&.Node().Properties, layered.InternalEdgeConstraint, layered.EdgeConstraintOutgoingOnly)
		reverseNodeEdges(, , , , )
	}

	for ,  := range  {
		 := .Node()
		 := basegraph.Get(&.Properties, layered.PortConstraintsOption)
		if !.IsSideFixed() || len(.Ports) == 0 {
			continue
		}
		 := true
		for ,  := range .Ports {
			 := .Port()
			 := len(.IncomingEdges) - len(.OutgoingEdges)
			if !(.Side == layered.EastSide &&  > 0 ||
				.Side == layered.WestSide &&  < 0) {
				 = false
				break
			}
			for ,  := range .OutgoingEdges {
				 := basegraph.Get(
					&.Node(.EdgeTargetNode()).Properties, layered.LayerConstraintOption,
				)
				if  == layered.LayerConstraintLast ||  == layered.LayerConstraintLastSeparate {
					 = false
					break
				}
			}
			for ,  := range .IncomingEdges {
				 := basegraph.Get(
					&.Node(.EdgeSourceNode()).Properties, layered.LayerConstraintOption,
				)
				if  == layered.LayerConstraintFirst ||  == layered.LayerConstraintFirstSeparate {
					 = false
					break
				}
			}
		}
		if  {
			reverseNodeEdges(, , , layered.LayerConstraintNone, layered.PortTypeUndefined)
		}
	}
}

func constraintPortType( layered.LayerConstraint) (layered.PortType, bool) {
	switch  {
	case layered.LayerConstraintFirst, layered.LayerConstraintFirstSeparate:
		return layered.PortTypeOutput, true
	case layered.LayerConstraintLast, layered.LayerConstraintLastSeparate:
		return layered.PortTypeInput, true
	default:
		return layered.PortTypeUndefined, false
	}
}

func reverseNodeEdges(
	 *layered.LGraphArena,
	 layered.LGraphID,
	 layered.LNodeID,
	 layered.LayerConstraint,
	 layered.PortType,
) {
	for ,  := range append([]layered.LPortID(nil), .Node().Ports...) {
		if  == layered.PortTypeInput ||  == layered.PortTypeUndefined {
			for ,  := range append([]layered.LEdgeID(nil), .Port().OutgoingEdges...) {
				if canReverseConstraintOutgoing(, , ) {
					.ReverseEdge(, , true)
				}
			}
		}
		if  == layered.PortTypeOutput ||  == layered.PortTypeUndefined {
			for ,  := range append([]layered.LEdgeID(nil), .Port().IncomingEdges...) {
				if canReverseConstraintIncoming(, , ) {
					.ReverseEdge(, , true)
				}
			}
		}
	}
}

func canReverseConstraintOutgoing(
	 *layered.LGraphArena,
	 layered.LEdgeID,
	 layered.LayerConstraint,
) bool {
	 := .Edge()
	if basegraph.Get(&.Properties, layered.Reversed) {
		return false
	}
	 := .Node(.EdgeTargetNode())
	if  == layered.LayerConstraintLast && .Type == layered.LabelNode {
		return false
	}
	return basegraph.Get(&.Properties, layered.LayerConstraintOption) != layered.LayerConstraintLastSeparate
}

func canReverseConstraintIncoming(
	 *layered.LGraphArena,
	 layered.LEdgeID,
	 layered.LayerConstraint,
) bool {
	 := .Edge()
	if basegraph.Get(&.Properties, layered.Reversed) {
		return false
	}
	 := .Node(.EdgeSourceNode())
	if  == layered.LayerConstraintFirst && .Type == layered.LabelNode {
		return false
	}
	return basegraph.Get(&.Properties, layered.LayerConstraintOption) != layered.LayerConstraintFirstSeparate
}