// Copyright (c) 2016 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 wrapping

import (
	basegraph 
	
	
)

// ProcessBreakingPoints folds the chunks marked by InsertBreakingPoints onto
// the initial layers and optionally applies ELK's two dummy-chain shortening
// passes.
func ( *layered.LGraphArena,  layered.LGraphID) {
	performWrapping(, )
	if !basegraph.Get(&.Graph().Properties, layered.WrappingMultiEdgeImproveWrappedEdges) {
		return
	}
	for ,  := range .Graph().Layers {
		for ,  := range .Layer().Nodes {
			.Node().ID = 
		}
	}
	improveMultiCutIndexEdges(, )
	improveUnnecessarilyLongEdges(, , true)
	improveUnnecessarilyLongEdges(, , false)
}

func performWrapping( *layered.LGraphArena,  layered.LGraphID) {
	 := .Graph()
	 := .AddLayer()
	.Layers = insertLayer(.Layers, 0, )
	 := false
	 := 1
	 := 1
	for  < len(.Layers) {
		 := .Layers[]
		++
		 := .Layers[]
		 := append([]layered.LNodeID(nil), .Layer().Nodes...)
		 := len()
		for ,  := range  {
			.SetNodeLayer(, &)
		}
		if  {
			for  := len() - 1;  >= 0; -- {
				 := []
				 := append([]layered.LEdgeID(nil), .NodeIncomingEdges()...)
				for ,  := range  {
					.ReverseEdge(, , true)
					basegraph.Set(&.Properties, layered.Cyclic, true)
					 := InsertDummies(, , , )
					 := basegraph.Get(&.Node().Properties, breakingPointInfo)
					 := [len()-1]
					 := .EdgeSourceNode()
					 := .EdgeTargetNode()
					.StartInLayerDummy = &
					.StartInLayerEdge = &
					.EndInLayerDummy = &
					 := 
					.EndInLayerEdge = &
				}
			}
			 = false
		} else if len() > 0 && .Node([0]).Type == layered.BreakingPointNode {
			 = true
			 = -1
		}
		++
	}
	 := .Layers[:0]
	for ,  := range .Layers {
		if len(.Layer().Nodes) > 0 {
			 = append(, )
		}
	}
	.Layers = 
}

func improveMultiCutIndexEdges( *layered.LGraphArena,  layered.LGraphID) {
	for ,  := range .Graph().Layers {
		for ,  := range append([]layered.LNodeID(nil), .Layer().Nodes...) {
			if !IsBreakingPointStart(, ) {
				continue
			}
			 := basegraph.Get(&.Node().Properties, breakingPointInfo)
			if .Prev != nil || .Next == nil {
				continue
			}
			 := 
			 := .Next
			for  != nil {
				dropDummies(, .Start, *.StartInLayerDummy, false, true)
				updateIndexesAfter(, .End)
				updateIndexesAfter(, .Start)
				updateIndexesAfter(, *.StartInLayerDummy)
				updateIndexesAfter(, *.EndInLayerDummy)

				 := *.Edge(*.EndInLayerEdge).Target
				.SetEdgeTarget(*.EndInLayerEdge, &)
				.SetEdgeTarget(*.EndInLayerEdge, nil)
				.SetNodeLayer(.End, nil)
				.SetNodeLayer(.Start, nil)
				.SetNodeLayer(*.StartInLayerDummy, nil)
				.SetNodeLayer(*.EndInLayerDummy, nil)

				 := &BPInfo{
					Start: .Start, End: .End,
					NodeStartEdge:     .NodeStartEdge,
					StartEndEdge:      .StartEndEdge,
					OriginalEdge:      .OriginalEdge,
					StartInLayerDummy: .StartInLayerDummy,
					StartInLayerEdge:  .StartInLayerEdge,
					EndInLayerDummy:   .EndInLayerDummy,
					EndInLayerEdge:    .EndInLayerEdge,
					Prev:              .Prev,
					Next:              .Next,
				}
				basegraph.Set(&.Node(.Start).Properties, breakingPointInfo, )
				basegraph.Set(&.Node(.End).Properties, breakingPointInfo, )
				 = .Next
				 = 
			}
		}
	}
}

func improveUnnecessarilyLongEdges(
	 *layered.LGraphArena,
	 layered.LGraphID,
	 bool,
) {
	 := false
	for {
		 = false
		 := .Graph().Layers
		for  := 0;  < len(); ++ {
			 := 
			if  {
				 = len() - 1 - 
			}
			 := append([]layered.LNodeID(nil), .Layer([]).Nodes...)
			// ELK 0.8.1 calls Lists.reverse(nodes) here without retaining the
			// returned view, so the copied list remains in forward order.
			for ,  := range  {
				 := IsBreakingPointStart(, )
				if  {
					 = IsBreakingPointEnd(, )
				}
				if ! {
					continue
				}
				 := basegraph.Get(&.Node().Properties, breakingPointInfo)
				 := *.StartInLayerDummy
				if  {
					 = *.EndInLayerDummy
				}
				// Source assignment is intentional: ELK only retains the result
				// of the last matching node in this traversal.
				 = dropDummies(, , , , false)
			}
		}
		if ! {
			return
		}
	}
}

func dropDummies(
	 *layered.LGraphArena,
	 layered.LNodeID,
	 layered.LNodeID,
	,  bool,
) bool {
	,  := nextLongEdgeDummy(, , )
	,  := nextLongEdgeDummy(, , )
	 := false
	for  &&  {
		if ! && !adjacentOrSeparatedByBreakingPoints(, , , ) {
			break
		}
		,  := nextLongEdgeDummy(, , )
		,  := nextLongEdgeDummy(, , )
		updateIndexesAfter(, )
		updateIndexesAfter(, )
		 := *.Node().Layer
		,  := .Node().ID, .Node().ID
		intermediate.JoinLongEdgeAt(, , false)
		intermediate.JoinLongEdgeAt(, , false)
		if  {
			.SetNodeLayerAt(, &, )
			.Node().ID = 
			.SetNodeLayerAt(, &, +1)
			.Node().ID = 
		} else {
			.SetNodeLayerAt(, &, )
			.Node().ID = 
			.SetNodeLayerAt(, &, +1)
			.Node().ID = 
		}
		.SetNodeLayer(, nil)
		.SetNodeLayer(, nil)
		,  = , 
		,  = , 
		 = true
	}
	return 
}

func adjacentOrSeparatedByBreakingPoints(
	 *layered.LGraphArena,
	,  layered.LNodeID,
	 bool,
) bool {
	,  := , 
	if  {
		,  = , 
	}
	 := .Layer(*.Node().Layer)
	for  := .Node().ID + 1;  < .Node().ID; ++ {
		 := .Nodes[]
		if .Node().Type != layered.BreakingPointNode && !isInLayerDummy(, ) {
			return false
		}
	}
	return true
}

func nextLongEdgeDummy(
	 *layered.LGraphArena,
	 layered.LNodeID,
	 bool,
) (layered.LNodeID, bool) {
	 := .NodeIncomingEdges()
	if  {
		 = .NodeOutgoingEdges()
	}
	for ,  := range  {
		 := .EdgeOtherNode(, )
		if .Node().Type == layered.LongEdgeNode &&
			*.Node().Layer != *.Node().Layer {
			return , true
		}
	}
	return 0, false
}

func isInLayerDummy( *layered.LGraphArena,  layered.LNodeID) bool {
	if .Node().Type != layered.LongEdgeNode {
		return false
	}
	for ,  := range .NodeConnectedEdges() {
		if !.EdgeIsSelfLoop() {
			 := .EdgeOtherNode(, )
			if .Node().Layer != nil && .Node().Layer != nil &&
				*.Node().Layer == *.Node().Layer {
				return true
			}
		}
	}
	return false
}

func updateIndexesAfter( *layered.LGraphArena,  layered.LNodeID) {
	 := .Layer(*.Node().Layer)
	for  := .Node().ID + 1;  < len(.Nodes); ++ {
		.Node(.Nodes[]).ID--
	}
}