// Copyright (c) 2014, 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 p3order

import 

// AllCrossingsCounter coordinates the specialized counters and implements
// ELK's IInitializable traversal callbacks explicitly.
type AllCrossingsCounter struct {
	crossingCounter          *CrossingsCounter
	hasHyperEdgesEastOfIndex []bool
	hasNorthSouthPorts       []bool
	inLayerEdgeCounts        []int
	nPorts                   int
}

func ( int) *AllCrossingsCounter {
	return &AllCrossingsCounter{
		hasHyperEdgesEastOfIndex: make([]bool, ),
		hasNorthSouthPorts:       make([]bool, ),
		inLayerEdgeCounts:        make([]int, ),
	}
}

func ( *AllCrossingsCounter) (
	 *layered.LGraphArena,
	 [][]layered.LNodeID,
) int {
	if len() == 0 {
		return 0
	}
	if .crossingCounter == nil {
		panic("p3order: AllCrossingsCounter used before InitAfterTraversal")
	}
	 := .crossingCounter.CountInLayerCrossingsOnSide(, [0], layered.WestSide)
	 += .crossingCounter.CountInLayerCrossingsOnSide(, [len()-1], layered.EastSide)
	for  := range  {
		 += .countCrossingsAt(, , )
	}
	return 
}

func ( *AllCrossingsCounter) (
	 *layered.LGraphArena,
	 int,
	 [][]layered.LNodeID,
) int {
	 := 0
	 := []
	if  < len()-1 {
		 := [+1]
		if .hasHyperEdgesEastOfIndex[] {
			 = countHyperedgeCrossings(, .crossingCounter.PortPositions, , )
			 += .crossingCounter.CountInLayerCrossingsOnSide(, , layered.EastSide)
			 += .crossingCounter.CountInLayerCrossingsOnSide(, , layered.WestSide)
		} else {
			 = .crossingCounter.CountCrossingsBetweenLayers(, , )
		}
	}
	if .hasNorthSouthPorts[] {
		 += .crossingCounter.CountNorthSouthPortCrossingsInLayer(, )
	}
	return 
}

func ( *AllCrossingsCounter) (
	 *layered.LGraphArena,
	,  int,
	 [][]layered.LNodeID,
) {
	 := [][]
	.hasNorthSouthPorts[] = .hasNorthSouthPorts[] ||
		.Node().Type == layered.NorthSouthPortNode
}

func ( *AllCrossingsCounter) (
	 *layered.LGraphArena,
	, ,  int,
	 [][]layered.LNodeID,
) {
	 := .Node([][]).Ports[]
	.Port().ID = .nPorts
	.nPorts++
	 := .Port()
	if len(.OutgoingEdges)+len(.IncomingEdges) > 1 {
		if .Side == layered.EastSide {
			.hasHyperEdgesEastOfIndex[] = true
		} else if .Side == layered.WestSide &&  > 0 {
			.hasHyperEdgesEastOfIndex[-1] = true
		}
	}
}

func ( *AllCrossingsCounter) (
	 *layered.LGraphArena,
	, ,  int,
	 layered.LEdgeID,
	 [][]layered.LNodeID,
) {
	 := .Node([][]).Ports[]
	 := .Edge()
	if .Source != nil && *.Source ==  && .EdgeIsInLayer() {
		.inLayerEdgeCounts[]++
	}
}

func ( *AllCrossingsCounter) () {
	.crossingCounter = NewCrossingsCounter(make([]int, .nPorts))
}

// InitializeAllCrossingsCounter performs IInitializable.init's exact
// layer/node/port/edge nesting and connected-edge order.
func (
	 *AllCrossingsCounter,
	 *layered.LGraphArena,
	 [][]layered.LNodeID,
) {
	for ,  := range  {
		for ,  := range  {
			.InitAtNodeLevel(, , , )
			for ,  := range .Node().Ports {
				.InitAtPortLevel(, , , , )
				for ,  := range .PortConnectedEdges() {
					.InitAtEdgeLevel(, , , , , )
				}
			}
		}
	}
	.InitAfterTraversal()
}