// 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 (
	

	
)

type BKAligner struct {
	Arena *layered.LGraphArena
	Graph layered.LGraphID
	Info  *NeighborhoodInformation
}

func (
	 *layered.LGraphArena,
	 layered.LGraphID,
	 *NeighborhoodInformation,
) *BKAligner {
	return &BKAligner{Arena: , Graph: , Info: }
}

// VerticalAlignment groups nodes into blocks while avoiding type-1 conflict
// edges. The two median neighbors are visited in the ELK-prescribed order.
func ( *BKAligner) (
	 *BKAlignedLayout,
	 map[layered.LEdgeID]bool,
) {
	for ,  := range .Arena.Graph(.Graph).Layers {
		for ,  := range .Arena.Layer().Nodes {
			 := .Arena.Node().ID
			.Root[] = nodePointer()
			.Align[] = nodePointer()
			.InnerShift[] = floatPointer(0)
		}
	}
	 := append([]layered.LayerID(nil), .Arena.Graph(.Graph).Layers...)
	if .HDirection == HorizontalLeft {
		reverseLayers()
	}
	for ,  := range  {
		 := -1
		 := append([]layered.LNodeID(nil), .Arena.Layer().Nodes...)
		if .VDirection == VerticalUp {
			 = math.MaxInt
			reverseNodes()
		}
		for ,  := range  {
			 := .Info.LeftNeighbors[.Arena.Node().ID]
			if .HDirection == HorizontalLeft {
				 = .Info.RightNeighbors[.Arena.Node().ID]
			}
			if len() == 0 {
				continue
			}
			 := float64(len())
			 := int(math.Floor((+1)/2)) - 1
			 := int(math.Ceil((+1)/2)) - 1
			if .VDirection == VerticalUp {
				for  := ;  >= ; -- {
					 = .tryAlign(, , , [], , true)
				}
			} else {
				for  := ;  <= ; ++ {
					 = .tryAlign(, , , [], , false)
				}
			}
		}
	}
}

func ( *BKAligner) (
	 *BKAlignedLayout,
	 map[layered.LEdgeID]bool,
	 layered.LNodeID,
	 Neighbor,
	 int,
	 bool,
) int {
	 := .Arena.Node().ID
	if requireNode(.Align[], "align") !=  {
		return 
	}
	 := .Info.NodeIndex[.Arena.Node(.Node).ID]
	 :=  < 
	if  {
		 =  > 
	}
	if [.Edge] || ! {
		return 
	}
	 := .Arena.Node(.Node).ID
	.Align[] = nodePointer()
	 := requireNode(.Root[], "root")
	.Root[] = nodePointer()
	.Align[] = nodePointer()
	 := .Arena.Node().ID
	.OD[] = .OD[] && .Arena.Node().Type == layered.LongEdgeNode
	return 
}

// InsideBlockShift accounts for node sizes, margins, and port positions after
// the original BK block construction.
func ( *BKAligner) ( *BKAlignedLayout) {
	for ,  := range blockRoots(.Arena, .Graph, ) {
		 := .Arena.Node().Margin.Top
		 := .Arena.Node().Size.Y + .Arena.Node().Margin.Bottom
		.InnerShift[.Arena.Node().ID] = floatPointer(0)
		 := 
		for {
			 := requireNode(.Align[.Arena.Node().ID], "align")
			if  ==  {
				break
			}
			,  := edgeBetween(.Arena, , )
			if ! {
				panic("p4nodes: aligned nodes are not connected")
			}
			 := .Arena.Edge()
			,  := .Arena.Port(*.Source), .Arena.Port(*.Target)
			 := .Position.Y + .Anchor.Y - .Position.Y - .Anchor.Y
			if .HDirection == HorizontalLeft {
				 = .Position.Y + .Anchor.Y - .Position.Y - .Anchor.Y
			}
			 := requireFloat(.InnerShift[.Arena.Node().ID], "inner shift") + 
			.InnerShift[.Arena.Node().ID] = floatPointer()
			 = math.Max(, .Arena.Node().Margin.Top-)
			 = math.Max(, +.Arena.Node().Size.Y+.Arena.Node().Margin.Bottom)
			 = 
		}
		 = 
		for {
			 := .Arena.Node().ID
			.InnerShift[] = floatPointer(requireFloat(.InnerShift[], "inner shift") + )
			 = requireNode(.Align[], "align")
			if  ==  {
				break
			}
		}
		.BlockSize[.Arena.Node().ID] = floatPointer( + )
	}
}

func blockRoots(
	 *layered.LGraphArena,
	 layered.LGraphID,
	 *BKAlignedLayout,
) []layered.LNodeID {
	 := make([]layered.LNodeID, 0)
	 := make(map[layered.LNodeID]bool)
	for ,  := range .Graph().Layers {
		for ,  := range .Layer().Nodes {
			 := requireNode(.Root[.Node().ID], "root")
			if ![] {
				[] = true
				 = append(, )
			}
		}
	}
	return 
}

func edgeBetween(
	 *layered.LGraphArena,
	,  layered.LNodeID,
) (layered.LEdgeID, bool) {
	for ,  := range .NodeConnectedEdges() {
		if .EdgeOtherNode(, ) ==  {
			return , true
		}
	}
	return 0, false
}

func reverseLayers( []layered.LayerID) {
	for ,  := 0, len()-1;  < ; ,  = +1, -1 {
		[], [] = [], []
	}
}

func reverseNodes( []layered.LNodeID) {
	for ,  := 0, len()-1;  < ; ,  = +1, -1 {
		[], [] = [], []
	}
}