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

import (
	

	basegraph 
	
)

type classNode struct {
	classShift *float64
	node       layered.LNodeID
	outgoing   []classEdge
	indegree   int
}

type classEdge struct {
	separation float64
	target     int
}

type BKCompactor struct {
	threshold  *ThresholdStrategy
	sinkNodes  map[layered.LNodeID]int
	sinkOrder  []layered.LNodeID
	classNodes []classNode
}

func ( *layered.LGraphArena,  layered.LGraphID) *BKCompactor {
	 := basegraph.Get(&.Graph().Properties, layered.BKEdgeStraightening)
	return &BKCompactor{
		threshold: NewThresholdStrategy( == layered.EdgeStraighteningImprove),
		sinkNodes: make(map[layered.LNodeID]int),
	}
}

func ( *BKCompactor) (
	 *layered.LGraphArena,
	 layered.LGraphID,
	 *NeighborhoodInformation,
	 *BKAlignedLayout,
) {
	for ,  := range .Graph().Layers {
		for ,  := range .Layer().Nodes {
			 := .Node().ID
			.Sink[] = nodePointer()
			 := math.Inf(1)
			if .VDirection == VerticalUp {
				 = math.Inf(-1)
			}
			.Shift[] = floatPointer()
		}
	}
	clear(.sinkNodes)
	.sinkOrder = .sinkOrder[:0]
	.classNodes = .classNodes[:0]
	 := append([]layered.LayerID(nil), .Graph().Layers...)
	if .HDirection == HorizontalLeft {
		reverseLayers()
	}
	.threshold.Init()
	for  := range .Y {
		.Y[] = nil
	}
	for ,  := range  {
		 := append([]layered.LNodeID(nil), .Layer().Nodes...)
		if .VDirection == VerticalUp {
			reverseNodes()
		}
		for ,  := range  {
			if requireNode(.Root[.Node().ID], "root") ==  {
				.placeBlock(, , , , )
			}
		}
	}
	.placeClasses(, )
	for ,  := range  {
		for ,  := range .Layer().Nodes {
			 := .Node().ID
			 := requireNode(.Root[], "root")
			.Y[] = floatPointer(requireFloat(.Y[.Node().ID], "y"))
			if  ==  {
				 := requireNode(.Sink[], "sink")
				 := requireFloat(.Shift[.Node().ID], "shift")
				if .VDirection == VerticalUp && !math.IsInf(, -1) ||
					.VDirection == VerticalDown && !math.IsInf(, 1) {
					.Y[] = floatPointer(requireFloat(.Y[], "y") + )
				}
			}
		}
	}
	.threshold.PostProcess(, , )
}

func ( *BKCompactor) (
	 *layered.LGraphArena,
	 layered.LGraphID,
	 *NeighborhoodInformation,
	 *BKAlignedLayout,
	 layered.LNodeID,
) {
	 := .Node().ID
	if .Y[] != nil {
		return
	}
	 := true
	.Y[] = floatPointer(0)
	 := 
	 := math.Inf(1)
	if .VDirection == VerticalDown {
		 = math.Inf(-1)
	}
	for {
		 := .NodeIndex[.Node().ID]
		 := *.Node().Layer
		 := len(.Layer().Nodes)
		 := .VDirection == VerticalDown &&  > 0 ||
			.VDirection == VerticalUp &&  < -1
		if  {
			 :=  - 1
			if .VDirection == VerticalUp {
				 =  + 1
			}
			 := .Layer().Nodes[]
			 := requireNode(.Root[.Node().ID], "root")
			.(, , , , )
			 = .threshold.CalculateThreshold(, , , , )
			if requireNode(.Sink[], "sink") ==  {
				.Sink[] = nodePointer(requireNode(.Sink[.Node().ID], "sink"))
			}
			 := requireNode(.Sink[], "sink")
			 := requireNode(.Sink[.Node().ID], "sink")
			if  ==  {
				 := layered.VerticalSpacing(, , )
				 := requireFloat(.Y[], "y")
				var  float64
				if .VDirection == VerticalUp {
					 = requireFloat(.Y[.Node().ID], "y") +
						requireFloat(.InnerShift[.Node().ID], "inner shift") -
						.Node().Margin.Top -  - .Node().Margin.Bottom -
						.Node().Size.Y - requireFloat(.InnerShift[.Node().ID], "inner shift")
					 := math.Min(, )
					if ! {
						 = math.Min(, )
					}
					.Y[] = floatPointer()
				} else {
					 = requireFloat(.Y[.Node().ID], "y") +
						requireFloat(.InnerShift[.Node().ID], "inner shift") + .Node().Size.Y +
						.Node().Margin.Bottom +  + .Node().Margin.Top -
						requireFloat(.InnerShift[.Node().ID], "inner shift")
					 := math.Max(, )
					if ! {
						 = math.Max(, )
					}
					.Y[] = floatPointer()
				}
				 = false
			} else {
				 := basegraph.Get(&.Graph().Properties, layered.SpacingNodeNode)
				 := .classNode()
				 := .classNode()
				var  float64
				if .VDirection == VerticalUp {
					 = requireFloat(.Y[], "y") + requireFloat(.InnerShift[.Node().ID], "inner shift") +
						.Node().Size.Y + .Node().Margin.Bottom +  -
						(requireFloat(.Y[.Node().ID], "y") + requireFloat(.InnerShift[.Node().ID], "inner shift") - .Node().Margin.Top)
				} else {
					 = requireFloat(.Y[], "y") + requireFloat(.InnerShift[.Node().ID], "inner shift") -
						.Node().Margin.Top - requireFloat(.Y[.Node().ID], "y") -
						requireFloat(.InnerShift[.Node().ID], "inner shift") - .Node().Size.Y -
						.Node().Margin.Bottom - 
				}
				.addClassEdge(, , )
			}
		} else {
			 = .threshold.CalculateThreshold(, , , , )
		}
		 = requireNode(.Align[.Node().ID], "align")
		if  ==  {
			break
		}
	}
	.threshold.FinishBlock()
}

func ( *BKCompactor) ( *layered.LGraphArena,  *BKAlignedLayout) {
	 := make([]int, 0)
	for ,  := range .sinkOrder {
		 := .sinkNodes[]
		if .classNodes[].indegree == 0 {
			 = append(, )
		}
	}
	for len() > 0 {
		 := [0]
		 = [1:]
		if .classNodes[].classShift == nil {
			.classNodes[].classShift = floatPointer(0)
		}
		 := *.classNodes[].classShift
		for ,  := range .classNodes[].outgoing {
			 := &.classNodes[.target]
			 :=  + .separation
			if .classShift == nil {
				.classShift = floatPointer()
			} else if .VDirection == VerticalDown {
				.classShift = floatPointer(math.Min(*.classShift, ))
			} else {
				.classShift = floatPointer(math.Max(*.classShift, ))
			}
			.indegree--
			if .indegree == 0 {
				 = append(, .target)
			}
		}
	}
	for ,  := range .sinkOrder {
		 := .classNodes[.sinkNodes[]]
		.Shift[.Node(.node).ID] = .classShift
	}
}

func ( *BKCompactor) ( layered.LNodeID) int {
	if ,  := .sinkNodes[];  {
		return 
	}
	 := len(.classNodes)
	.classNodes = append(.classNodes, classNode{node: })
	.sinkNodes[] = 
	.sinkOrder = append(.sinkOrder, )
	return 
}

func ( *BKCompactor) (,  int,  float64) {
	.classNodes[].indegree++
	.classNodes[].outgoing = append(.classNodes[].outgoing, classEdge{, })
}