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

// GraphStats is the source GraphStats cache used by the graph-wrapping
// heuristics. ELK's estimates intentionally include one spacing quantum for
// every node and layer.
type GraphStats struct {
	arena   *layered.LGraphArena
	graphID layered.LGraphID

	spacing        float64
	inLayerSpacing float64

	DesiredAspectRatio float64
	LongestPath        int

	maxWidth  *float64
	maxHeight *float64
	sumWidth  *float64
	widths    []float64
	heights   []float64
	cuts      []bool
}

func ( *layered.LGraphArena,  layered.LGraphID) *GraphStats {
	 := .Graph()
	 := basegraph.Get(&.Properties, layered.AspectRatio)
	 := basegraph.Get(&.Properties, layered.WrappingCorrectionFactor)
	 :=  * 
	 := basegraph.Get(&.Properties, layered.DirectionOption)
	if  == layered.DirectionUp ||  == layered.DirectionDown {
		 = 1 / 
	}
	return &GraphStats{
		arena:              ,
		graphID:            ,
		spacing:            basegraph.Get(&.Properties, layered.SpacingNodeNodeBetweenLayers),
		inLayerSpacing:     basegraph.Get(&.Properties, layered.SpacingNodeNode),
		DesiredAspectRatio: ,
		LongestPath:        len(.Layers),
	}
}

func ( *GraphStats) ( layered.LayerID) float64 {
	return .determineLayerWidth()
}

func ( *GraphStats) () float64 { return .SumWidth() }

func ( *GraphStats) ( []int) float64 {
	if len() == 0 {
		return .ApproximateLayeringWidth()
	}
	 := 0.0
	 := 0
	for ,  := range  {
		 = math.Max(, .determineChunkWidth(, ))
		 = 
	}
	return math.Max(, .determineChunkWidth(, len(.arena.Graph(.graphID).Layers)))
}

func ( *GraphStats) ( layered.LayerID) float64 {
	return .determineLayerHeight()
}

func ( *GraphStats) () float64 { return .MaxHeight() }

func ( *GraphStats) ( []int) float64 {
	if len() == 0 {
		return 0
	}
	 := 0.0
	 := 0
	for ,  := range  {
		 += .determineChunkHeight(, )
		 = 
	}
	return  + .determineChunkHeight(, len(.arena.Graph(.graphID).Layers))
}

func ( *GraphStats) () float64 {
	if .maxWidth == nil {
		 := math.Inf(-1)
		for ,  := range .arena.Graph(.graphID).Layers {
			 = math.Max(, .determineLayerWidth())
		}
		.maxWidth = &
	}
	return *.maxWidth
}

func ( *GraphStats) () float64 {
	if .sumWidth == nil {
		 := 0.0
		for ,  := range .arena.Graph(.graphID).Layers {
			 += .determineLayerWidth()
		}
		.sumWidth = &
	}
	return *.sumWidth
}

func ( *GraphStats) () []float64 {
	.initWidthsAndHeights()
	return .widths
}

func ( *GraphStats) () []float64 {
	.initWidthsAndHeights()
	return .heights
}

func ( *GraphStats) () {
	if .widths != nil {
		return
	}
	.widths = make([]float64, .LongestPath)
	.heights = make([]float64, .LongestPath)
	for ,  := range .arena.Graph(.graphID).Layers {
		.widths[] = .determineLayerWidth()
		.heights[] = .determineLayerHeight()
	}
}

func ( *GraphStats) ( layered.LayerID) float64 {
	 := 0.0
	for ,  := range .arena.Layer().Nodes {
		 := .arena.Node()
		 := .Size.X + .Margin.Right + .Margin.Left + .spacing
		 = math.Max(, )
	}
	return 
}

func ( *GraphStats) (,  int) float64 {
	 := 0.0
	for ,  := range .arena.Graph(.graphID).Layers[:] {
		 += .determineLayerWidth()
	}
	return 
}

func ( *GraphStats) () float64 {
	if .maxHeight == nil {
		 := math.Inf(-1)
		for ,  := range .arena.Graph(.graphID).Layers {
			 = math.Max(, .determineLayerHeight())
		}
		.maxHeight = &
	}
	return *.maxHeight
}

func ( *GraphStats) ( layered.LayerID) float64 {
	 := 0.0
	for ,  := range .arena.Layer().Nodes {
		 := .arena.Node()
		 += .Size.Y + .Margin.Bottom + .Margin.Top + .inLayerSpacing
		for ,  := range .arena.NodeIncomingEdges() {
			 := .arena.EdgeSourceNode()
			if .arena.Node().Type != layered.NorthSouthPortNode {
				continue
			}
			,  := basegraph.TryGet(&.arena.Node().Properties, layered.OriginalElement)
			if ! || .Kind != layered.OriginLNode {
				continue
			}
			 := .arena.Node(.LNode)
			 += .Size.Y + .Margin.Bottom + .Margin.Top
		}
	}
	return 
}

func ( *GraphStats) (,  int) float64 {
	 := 0.0
	for ,  := range .arena.Graph(.graphID).Layers[:] {
		 = math.Max(, .determineLayerHeight())
	}
	return 
}

func ( *GraphStats) ( int) bool {
	.initCutsAllowed()
	return .cuts[]
}

func ( *GraphStats) () []bool {
	.initCutsAllowed()
	return .cuts
}

func ( *GraphStats) () {
	if .cuts != nil {
		return
	}
	.cuts = make([]bool, len(.arena.Graph(.graphID).Layers))
	if len(.cuts) == 0 {
		return
	}
	.cuts[0] = false
	 := .arena.Graph(.graphID)
	if ,  := basegraph.TryGet(&.Properties, layered.WrappingValidifyForbiddenIndices);  {
		// This preserves ELK 0.8.1 literally: the freshly allocated array is
		// never filled true in the explicit-forbidden-indices branch.
		for ,  := range  {
			if  > 0 &&  < len(.cuts) {
				.cuts[] = false
			}
		}
		return
	}
	for  := 1;  < len(.Layers); ++ {
		.cuts[] = .cutAllowedBefore(.Layers[])
	}
}

func ( *GraphStats) ( layered.LayerID) bool {
	var ,  *layered.LNodeID
	for ,  := range .arena.Layer().Nodes {
		for ,  := range .arena.NodeIncomingEdges() {
			if  != nil && * !=  {
				return false
			}
			 := 
			 = &
			 := .arena.EdgeSourceNode()
			if  != nil && * !=  {
				return false
			}
			 := 
			 = &
		}
	}
	return true
}