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

	
)

const straighteningThreshold = math.MaxFloat64

type postprocessable struct {
	free     layered.LNodeID
	isRoot   bool
	hasEdges bool
	edge     *layered.LEdgeID
}

type ThresholdStrategy struct {
	simple           bool
	blockFinished    map[layered.LNodeID]bool
	postprocessQueue []postprocessable
	postprocessStack []postprocessable
}

func ( bool) *ThresholdStrategy {
	return &ThresholdStrategy{simple: , blockFinished: make(map[layered.LNodeID]bool)}
}

func ( *ThresholdStrategy) () {
	clear(.blockFinished)
	.postprocessQueue = .postprocessQueue[:0]
	.postprocessStack = .postprocessStack[:0]
}

func ( *ThresholdStrategy) ( layered.LNodeID) {
	.blockFinished[] = true
}

func ( *ThresholdStrategy) (
	 *layered.LGraphArena,
	 *BKAlignedLayout,
	 float64,
	,  layered.LNodeID,
) float64 {
	if !.simple {
		if .VDirection == VerticalUp {
			return math.Inf(1)
		}
		return math.Inf(-1)
	}
	 :=  == 
	 := requireNode(.Align[.Node().ID], "align") == 
	if ! && ! {
		return 
	}
	 := 
	if  {
		 = .bound(, , , true)
	}
	if math.IsInf(, 0) &&  {
		 = .bound(, , , false)
	}
	return 
}

func ( *ThresholdStrategy) (
	 *layered.LGraphArena,
	 *BKAlignedLayout,
	 *postprocessable,
) {
	var  []layered.LEdgeID
	if .isRoot {
		if .HDirection == HorizontalRight {
			 = .NodeIncomingEdges(.free)
		} else {
			 = .NodeOutgoingEdges(.free)
		}
	} else if .HDirection == HorizontalLeft {
		 = .NodeIncomingEdges(.free)
	} else {
		 = .NodeOutgoingEdges(.free)
	}
	 := false
	for ,  := range  {
		 := requireNode(.Root[.Node(.free).ID], "root")
		if !.OD[.Node().ID] && .EdgeIsInLayer() {
			continue
		}
		if .SU[.Node().ID] {
			continue
		}
		 = true
		 := .EdgeOtherNode(, .free)
		 := requireNode(.Root[.Node().ID], "root")
		if .blockFinished[] {
			.hasEdges = true
			.edge = edgePointer()
			return
		}
	}
	.hasEdges = 
	.edge = nil
}

func ( *ThresholdStrategy) (
	 *layered.LGraphArena,
	 *BKAlignedLayout,
	 layered.LNodeID,
	 bool,
) float64 {
	 := math.Inf(-1)
	if .VDirection == VerticalUp {
		 = math.Inf(1)
	}
	 := postprocessable{free: , isRoot: }
	.pickEdge(, , &)
	if .edge == nil {
		if .hasEdges {
			.postprocessQueue = append(.postprocessQueue, )
		}
		return 
	}
	 := .Edge(*.edge)
	,  := *.Source, *.Target
	,  := , 
	if  {
		if .HDirection == HorizontalRight {
			,  = , 
		}
	} else if .HDirection == HorizontalLeft {
		,  = , 
	}
	 := *.Port().Node
	 := *.Port().Node
	 := requireNode(.Root[.Node().ID], "root")
	 := requireFloat(.Y[.Node().ID], "y") +
		requireFloat(.InnerShift[.Node().ID], "inner shift") +
		.Port().Position.Y + .Port().Anchor.Y -
		requireFloat(.InnerShift[.Node().ID], "inner shift") -
		.Port().Position.Y - .Port().Anchor.Y
	 := requireNode(.Root[.Node(*.Port().Node).ID], "root")
	 := requireNode(.Root[.Node(*.Port().Node).ID], "root")
	.SU[.Node().ID] = true
	.SU[.Node().ID] = true
	return 
}

func ( *ThresholdStrategy) (
	 *layered.LGraphArena,
	 *NeighborhoodInformation,
	 *BKAlignedLayout,
) {
	if !.simple {
		return
	}
	for len(.postprocessQueue) > 0 {
		 := .postprocessQueue[0]
		.postprocessQueue = .postprocessQueue[1:]
		.pickEdge(, , &)
		if .edge == nil {
			continue
		}
		 := requireNode(.Root[.Node(.free).ID], "root")
		if !.OD[.Node().ID] && .EdgeIsInLayer(*.edge) {
			continue
		}
		if !processPostprocessable(, , , ) {
			.postprocessStack = append(.postprocessStack, )
		}
	}
	for len(.postprocessStack) > 0 {
		 := len(.postprocessStack) - 1
		 := .postprocessStack[]
		.postprocessStack = .postprocessStack[:]
		processPostprocessable(, , , )
	}
}

func processPostprocessable(
	 *layered.LGraphArena,
	 *NeighborhoodInformation,
	 *BKAlignedLayout,
	 postprocessable,
) bool {
	 := *.edge
	 := .Edge()
	,  := *.Target, *.Source
	if .EdgeSourceNode() != .free {
		,  = *.Source, *.Target
	}
	 := .CalculateDelta(, , )
	 := *.Port().Node
	if  > 0 &&  < straighteningThreshold {
		 := .CheckSpaceAbove(, , , )
		.ShiftBlock(, , -)
		return  > 0
	}
	if  < 0 && - < straighteningThreshold {
		 := .CheckSpaceBelow(, , -, )
		.ShiftBlock(, , )
		return  > 0
	}
	return false
}

func edgePointer( layered.LEdgeID) *layered.LEdgeID { return & }