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

// SwitchDecider compares the crossing count of two adjacent nodes in their
// current and switched orders.
type SwitchDecider struct {
	freeLayer                        []layered.LNodeID
	leftInLayerCounter               *CrossingsCounter
	rightInLayerCounter              *CrossingsCounter
	northSouthCounter                *NorthSouthEdgeNeighbouringNodeCrossingsCounter
	crossingMatrixFiller             *CrossingMatrixFiller
	parentCrossCounter               *CrossingsCounter
	countCrossingsCausedByPortSwitch bool
}

func (
	 *layered.LGraphArena,
	 int,
	 [][]layered.LNodeID,
	 *CrossingMatrixFiller,
	 []int,
	 *GraphInfoHolder,
	 bool,
) *SwitchDecider {
	if  >= len() {
		panic("p3order: greedy SwitchDecider free layer is not in graph")
	}
	 := []
	 := &SwitchDecider{
		freeLayer:            ,
		leftInLayerCounter:   NewCrossingsCounter(),
		rightInLayerCounter:  NewCrossingsCounter(),
		crossingMatrixFiller: ,
	}
	.leftInLayerCounter.InitPortPositionsForInLayerCrossings(, , layered.WestSide)
	.rightInLayerCounter.InitPortPositionsForInLayerCrossings(, , layered.EastSide)
	.northSouthCounter = NewNorthSouthEdgeNeighbouringNodeCrossingsCounter(, )
	.countCrossingsCausedByPortSwitch = ! && .HasParent && !.DontSweepInto() &&
		.Node([0]).Type == layered.ExternalPortNode
	if .countCrossingsCausedByPortSwitch {
		.initParentCrossingsCounter(, , len(), )
	}
	return 
}

func ( *SwitchDecider) (
	 *layered.LGraphArena,
	,  int,
	 *GraphInfoHolder,
) {
	 := .ParentGraphData
	 := .CurrentNodeOrder
	.parentCrossCounter = NewCrossingsCounter(.PortPositions)
	 := .Layer(*.Node(*.Parent).Layer).ID
	var ,  []layered.LNodeID
	if  > 0 {
		 = [-1]
	}
	 := []
	if  < len()-1 {
		 = [+1]
	}
	if  == -1 {
		.parentCrossCounter.InitForCountingBetween(, , )
	} else {
		.parentCrossCounter.InitForCountingBetween(, , )
	}
}

func ( *SwitchDecider) (
	 *layered.LGraphArena,
	,  layered.LNodeID,
) {
	.leftInLayerCounter.SwitchNodes(, , , layered.WestSide)
	.rightInLayerCounter.SwitchNodes(, , , layered.EastSide)
	if .countCrossingsCausedByPortSwitch {
		.parentCrossCounter.SwitchPorts(
			, externalDummyOriginPort(, ), externalDummyOriginPort(, ),
		)
	}
}

func ( *SwitchDecider) (
	 *layered.LGraphArena,
	,  int,
) bool {
	if .constraintsPreventSwitch(, , ) {
		return false
	}
	,  := .freeLayer[], .freeLayer[]
	,  := .leftInLayerCounter.
		CountInLayerCrossingsBetweenNodesInBothOrders(, , , layered.WestSide)
	,  := .rightInLayerCounter.
		CountInLayerCrossingsBetweenNodesInBothOrders(, , , layered.EastSide)
	.northSouthCounter.CountCrossings(, , )
	 := .crossingMatrixFiller.GetCrossingMatrixEntry(, , ) +
		 +  + .northSouthCounter.UpperLowerCrossings
	 := .crossingMatrixFiller.GetCrossingMatrixEntry(, , ) +
		 +  + .northSouthCounter.LowerUpperCrossings
	if .countCrossingsCausedByPortSwitch {
		,  := .parentCrossCounter.CountCrossingsBetweenPortsInBothOrders(
			, externalDummyOriginPort(, ), externalDummyOriginPort(, ),
		)
		 += 
		 += 
	}
	return  > 
}

func ( *SwitchDecider) (
	 *layered.LGraphArena,
	,  int,
) bool {
	,  := .freeLayer[], .freeLayer[]
	return haveSuccessorConstraint(, , ) ||
		haveLayoutUnitConstraint(, , ) ||
		areNormalAndNorthSouthPortDummy(, , )
}

func haveSuccessorConstraint(
	 *layered.LGraphArena,
	,  layered.LNodeID,
) bool {
	for ,  := range basegraph.Get(&.Node().Properties, layered.InLayerSuccessorConstraints) {
		if  ==  {
			return true
		}
	}
	return false
}

func haveLayoutUnitConstraint(
	 *layered.LGraphArena,
	,  layered.LNodeID,
) bool {
	 := .Node().Type != layered.LongEdgeNode &&
		.Node().Type != layered.LongEdgeNode
	,  := basegraph.TryGet(&.Node().Properties, layered.InLayerLayoutUnit)
	,  := basegraph.TryGet(&.Node().Properties, layered.InLayerLayoutUnit)
	 :=  !=  ||  != 
	 :=  &&  !=  ||  &&  != 
	 := hasEdgesOnSide(, , layered.NorthSide)
	 := hasEdgesOnSide(, , layered.SouthSide)
	 =  || hasEdgesOnSide(, , layered.SouthSide) ||
		hasEdgesOnSide(, , layered.NorthSide)
	return  && ( &&  ||  || )
}

func hasEdgesOnSide(
	 *layered.LGraphArena,
	 layered.LNodeID,
	 layered.PortSide,
) bool {
	for ,  := range .NodePortsOnSide(, ) {
		if basegraph.Has(&.Port().Properties, layered.PortDummy) ||
			len(.PortConnectedEdges()) > 0 {
			return true
		}
	}
	return false
}

func areNormalAndNorthSouthPortDummy(
	 *layered.LGraphArena,
	,  layered.LNodeID,
) bool {
	return .Node().Type == layered.NorthSouthPortNode && .Node().Type == layered.NormalNode ||
		.Node().Type == layered.NorthSouthPortNode && .Node().Type == layered.NormalNode
}

func externalDummyOriginPort( *layered.LGraphArena,  layered.LNodeID) layered.LPortID {
	,  := basegraph.TryGet(&.Node().Properties, layered.OriginalElement)
	if ! || .Kind != layered.OriginLPort {
		panic("p3order: external port dummy has no LPort origin")
	}
	return .LPort
}