// Copyright (c) 2010, 2015 Kiel University and others.
// Copyright (c) 2012, 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 
	
)

type PortDistributorKind uint8

const (
	NodeRelativePortDistributor PortDistributorKind = iota
	LayerTotalPortDistributor
)

type BarycenterPortDistributor struct {
	Kind              PortDistributorKind
	PortRanks         []float32
	minimumBarycenter float32
	maximumBarycenter float32
	nodePositions     [][]int
	portBarycenter    []float32
	inLayerPorts      []layered.LPortID
	nPorts            int
}

func ( PortDistributorKind,  int) *BarycenterPortDistributor {
	return &BarycenterPortDistributor{Kind: , nodePositions: make([][]int, )}
}

func ( *BarycenterPortDistributor) (
	 *layered.LGraphArena,
	 [][]layered.LNodeID,
	 int,
	 bool,
) bool {
	.updateNodePositions(, [])
	 := append([]layered.LNodeID(nil), []...)
	 := layered.EastSide
	if  {
		 = layered.WestSide
	}
	if !isFirstSweepLayer(len(), , ) {
		 :=  + 1
		if  {
			 =  - 1
		}
		 := append([]layered.LNodeID(nil), []...)
		.CalculatePortRanks(, , portTypeForSweep())
		for ,  := range  {
			.distributePorts(, , )
		}
		.CalculatePortRanks(, , portTypeForSweep(!))
		for ,  := range  {
			if .Node().NestedGraph == nil {
				.distributePorts(, , .Opposed())
			}
		}
	} else {
		for ,  := range  {
			.distributePorts(, , )
		}
	}
	return false
}

func ( *BarycenterPortDistributor) (
	 *layered.LGraphArena,
	 []layered.LNodeID,
	 layered.PortType,
) {
	var  float32
	for ,  := range  {
		 += .calculateNodePortRanks(, , , )
	}
}

func ( *BarycenterPortDistributor) (
	 *layered.LGraphArena,
	 layered.LNodeID,
	 float32,
	 layered.PortType,
) float32 {
	 := .Node().Ports
	switch .Kind {
	case NodeRelativePortDistributor:
		switch  {
		case layered.PortTypeInput:
			,  := 0, 0
			for ,  := range  {
				if len(.Port().IncomingEdges) > 0 {
					++
					if .Port().Side == layered.NorthSide {
						++
					}
				}
			}
			 := float32(1) / float32(+1)
			 :=  + float32()*
			 :=  + 1 - 
			for ,  := range inputPorts(, ) {
				if .Port().Side == layered.NorthSide {
					.PortRanks[.Port().ID] = 
					 -= 
				} else {
					.PortRanks[.Port().ID] = 
					 -= 
				}
			}
		case layered.PortTypeOutput:
			 := 0
			for ,  := range  {
				if len(.Port().OutgoingEdges) > 0 {
					++
				}
			}
			 := float32(1) / float32(+1)
			 :=  + 
			for ,  := range outputPorts(, ) {
				.PortRanks[.Port().ID] = 
				 += 
			}
		default:
			panic("p3order: undefined port type")
		}
		return 1
	case LayerTotalPortDistributor:
		switch  {
		case layered.PortTypeInput:
			,  := 0, 0
			for ,  := range  {
				if len(.Port().IncomingEdges) > 0 {
					++
					if .Port().Side == layered.NorthSide {
						++
					}
				}
			}
			 :=  + float32()
			 :=  + float32()
			for ,  := range inputPorts(, ) {
				if .Port().Side == layered.NorthSide {
					.PortRanks[.Port().ID] = 
					--
				} else {
					.PortRanks[.Port().ID] = 
					--
				}
			}
			return float32()
		case layered.PortTypeOutput:
			 := 0
			for ,  := range outputPorts(, ) {
				++
				.PortRanks[.Port().ID] =  + float32()
			}
			return float32()
		default:
			panic("p3order: undefined port type")
		}
	default:
		panic("p3order: unknown port distributor")
	}
}

func ( *BarycenterPortDistributor) (
	 *layered.LGraphArena,
	 layered.LNodeID,
	 layered.PortSide,
) {
	 := basegraph.Get(&.Node().Properties, layered.PortConstraintsOption)
	if .IsOrderFixed() {
		return
	}
	for ,  := range []layered.PortSide{, layered.SouthSide, layered.NorthSide} {
		.distributePortList(, , .NodePortsOnSide(, ))
	}
	 := .Node().Ports
	sort.SliceStable(, func(,  int) bool {
		,  := .Port([]), .Port([])
		if .Side != .Side {
			return .Side < .Side
		}
		 := .portBarycenter[.ID]
		 := .portBarycenter[.ID]
		if  == 0 &&  == 0 {
			return false
		}
		if  == 0 {
			return true
		}
		if  == 0 {
			return false
		}
		return  < 
	})
	.Node().Ports = 
	.InvalidatePortSideCache()
}

func ( *BarycenterPortDistributor) (
	 *layered.LGraphArena,
	 layered.LNodeID,
	 []layered.LPortID,
) {
	.inLayerPorts = .inLayerPorts[:0]
	.collectPortBarycenters(, , )
	if len(.inLayerPorts) > 0 {
		.calculateInLayerPortBarycenters(, )
	}
}

func ( *BarycenterPortDistributor) (
	 *layered.LGraphArena,
	 layered.LNodeID,
	 []layered.LPortID,
) {
	.minimumBarycenter = 0
	.maximumBarycenter = 0
	 := float32(2*len(.Layer(*.Node().Layer).Nodes) + 1)
	for ,  := range  {
		 := .Port()
		 := .Side == layered.NorthSide || .Side == layered.SouthSide
		var  float32
		if  {
			,  := basegraph.TryGet(&.Properties, layered.PortDummy)
			if ! {
				continue
			}
			 += .northSouthPortValue(, , , )
		} else {
			 := false
			for ,  := range .OutgoingEdges {
				 := *.Edge().Target
				if sameLayer(.Node(*.Port().Node).Layer, .Node().Layer) {
					.inLayerPorts = append(.inLayerPorts, )
					 = true
					break
				}
				 += .PortRanks[.Port().ID]
			}
			if  {
				continue
			}
			for ,  := range .IncomingEdges {
				 := *.Edge().Source
				if sameLayer(.Node(*.Port().Node).Layer, .Node().Layer) {
					.inLayerPorts = append(.inLayerPorts, )
					 = true
					break
				}
				 -= .PortRanks[.Port().ID]
			}
			if  {
				continue
			}
		}
		 := len(.PortConnectedEdges())
		if  > 0 {
			 :=  / float32()
			.portBarycenter[.ID] = 
			if  < .minimumBarycenter {
				.minimumBarycenter = 
			}
			if  > .maximumBarycenter {
				.maximumBarycenter = 
			}
		} else if  {
			.portBarycenter[.ID] = 
		}
	}
}

func ( *BarycenterPortDistributor) (
	 *layered.LGraphArena,
	 layered.LNodeID,
) {
	 := .positionOf(, ) + 1
	 := len(.Layer(*.Node().Layer).Nodes) + 1
	for ,  := range .inLayerPorts {
		,  := 0, 0
		for ,  := range .PortConnectedEdges() {
			 := .EdgeOtherPort(, )
			 := *.Port().Node
			if sameLayer(.Node().Layer, .Node().Layer) {
				 += .positionOf(, ) + 1
				++
			}
		}
		 := float32() / float32()
		 := .Port()
		if .Side == layered.EastSide {
			if  < float32() {
				.portBarycenter[.ID] = .minimumBarycenter - 
			} else {
				.portBarycenter[.ID] = .maximumBarycenter + float32() - 
			}
		} else if .Side == layered.WestSide {
			if  < float32() {
				.portBarycenter[.ID] = .maximumBarycenter + 
			} else {
				.portBarycenter[.ID] = .minimumBarycenter - (float32() - )
			}
		}
	}
}

func ( *BarycenterPortDistributor) (
	 *layered.LGraphArena,
	 float32,
	 layered.LPortID,
	 layered.LNodeID,
) float32 {
	,  := false, false
	for ,  := range .Node().Ports {
		,  := basegraph.TryGet(&.Port().Properties, layered.OriginalElement)
		if ! || .Kind != layered.OriginLPort || .LPort !=  {
			continue
		}
		if len(.Port().OutgoingEdges) > 0 {
			 = true
		} else if len(.Port().IncomingEdges) > 0 {
			 = true
		}
	}
	 := float32(.positionOf(, ))
	if  !=  {
		if  {
			if .Port().Side == layered.NorthSide {
				return -
			}
			return  - 
		}
		return  + 1
	}
	if  &&  && .Port().Side == layered.SouthSide {
		return  / 2
	}
	return 0
}

func ( *BarycenterPortDistributor) (
	 *layered.LGraphArena,
	 layered.LNodeID,
) int {
	return .nodePositions[.Layer(*.Node().Layer).ID][.Node().ID]
}

func ( *BarycenterPortDistributor) (
	 *layered.LGraphArena,
	 []layered.LNodeID,
) {
	for ,  := range  {
		.nodePositions[.Layer(*.Node().Layer).ID][.Node().ID] = 
	}
}

func ( *BarycenterPortDistributor) ( int,  [][]layered.LNodeID) {
	.nodePositions[] = make([]int, len([]))
}

func ( *BarycenterPortDistributor) (
	 *layered.LGraphArena,
	,  int,
	 [][]layered.LNodeID,
) {
	 := [][]
	.Node().ID = 
	.nodePositions[][] = 
}

func ( *BarycenterPortDistributor) (
	 *layered.LGraphArena,
	, ,  int,
	 [][]layered.LNodeID,
) {
	 := .Node([][]).Ports[]
	.Port().ID = .nPorts
	.nPorts++
}

func ( *BarycenterPortDistributor) () {
	.PortRanks = make([]float32, .nPorts)
	.portBarycenter = make([]float32, .nPorts)
}

func (
	 *BarycenterPortDistributor,
	 *layered.LGraphArena,
	 [][]layered.LNodeID,
) {
	for ,  := range  {
		.InitAtLayerLevel(, )
		if len() > 0 && .Node([0]).Layer != nil {
			.Layer(*.Node([0]).Layer).ID = 
		}
		for ,  := range  {
			.InitAtNodeLevel(, , , )
			for  := range .Node().Ports {
				.InitAtPortLevel(, , , , )
			}
		}
	}
	.InitAfterTraversal()
}

func inputPorts( *layered.LGraphArena,  layered.LNodeID) []layered.LPortID {
	 := make([]layered.LPortID, 0)
	for ,  := range .Node().Ports {
		if len(.Port().IncomingEdges) > 0 {
			 = append(, )
		}
	}
	return 
}

func outputPorts( *layered.LGraphArena,  layered.LNodeID) []layered.LPortID {
	 := make([]layered.LPortID, 0)
	for ,  := range .Node().Ports {
		if len(.Port().OutgoingEdges) > 0 {
			 = append(, )
		}
	}
	return 
}

func portTypeForSweep( bool) layered.PortType {
	if  {
		return layered.PortTypeOutput
	}
	return layered.PortTypeInput
}

func isFirstSweepLayer(,  int,  bool) bool {
	if  {
		return  == 0
	}
	return  == -1
}