// Copyright (c) 2011, 2018 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 intermediate

import (
	
	

	basegraph 
	
)

// SortPortLists puts ports into clockwise order starting with the leftmost
// northern port. Sort stability intentionally preserves model order on ties.
func ( *layered.LGraphArena,  layered.LGraphID) error {
	 := basegraph.Get(&.Graph().Properties, layered.PortSortingStrategyOption)
	for ,  := range .GraphAllNodes() {
		 := .Node()
		 := basegraph.Get(&.Properties, layered.PortConstraintsOption)
		if .IsOrderFixed() {
			var  error
			sort.SliceStable(.Ports, func(,  int) bool {
				,  := compareFixedPorts(, .Ports[], .Ports[])
				if  != nil {
					 = 
					return false
				}
				return  < 0
			})
			if  != nil {
				return 
			}
		} else if .IsSideFixed() {
			sort.SliceStable(.Ports, func(,  int) bool {
				return .Port(.Ports[]).Side < .Port(.Ports[]).Side
			})
			reversePortSide(, .Ports, layered.SouthSide)
			reversePortSide(, .Ports, layered.WestSide)
			if  == layered.PortSortingPortDegree {
				sort.SliceStable(.Ports, func(,  int) bool {
					,  := .Port(.Ports[]), .Port(.Ports[])
					if .Side != .Side {
						return false
					}
					switch .Side {
					case layered.EastSide:
						return realPortDegree(, .Ports[], false) >
							realPortDegree(, .Ports[], false)
					case layered.WestSide:
						return realPortDegree(, .Ports[], true) <
							realPortDegree(, .Ports[], true)
					default:
						return false
					}
				})
			}
		}
		.CachePortSides()
	}
	return nil
}

func realPortDegree( *layered.LGraphArena,  layered.LPortID,  bool) int {
	 := .Port().OutgoingEdges
	if  {
		 = .Port().IncomingEdges
	}
	 := 0
	for ,  := range  {
		if !basegraph.Get(&.Edge().Properties, layered.Reversed) {
			++
		}
	}
	return 
}

func compareFixedPorts( *layered.LGraphArena, ,  layered.LPortID) (int, error) {
	,  := .Port(), .Port()
	if .Side != .Side {
		if .Side < .Side {
			return -1, nil
		}
		return 1, nil
	}
	var ,  float64
	switch .Side {
	case layered.NorthSide:
		,  = .Position.X, .Position.X
	case layered.EastSide:
		,  = .Position.Y, .Position.Y
	case layered.SouthSide:
		,  = -.Position.X, -.Position.X
	case layered.WestSide:
		,  = -.Position.Y, -.Position.Y
	default:
		return 0, fmt.Errorf("layered: cannot sort a port with undefined side")
	}
	if  <  {
		return -1, nil
	}
	if  >  {
		return 1, nil
	}
	return 0, nil
}

func reversePortSide( *layered.LGraphArena,  []layered.LPortID,  layered.PortSide) {
	,  := findPortSideRange(, , )
	// Match ELK 0.8.1's PortListSorter.reverse exactly. In particular, a
	// range of two ports is left alone and highIndex is exclusive.
	if  <= +2 {
		return
	}
	 := ( - ) / 2
	for  := 0;  < ; ++ {
		,  := +, --1
		[], [] = [], []
	}
}

// findPortSideRange intentionally mirrors ELK 0.8.1's implementation,
// including its use of the low-index port while advancing highIndex and its
// ports.size()-1 upper bound. These details affect the observable assignment
// of implicit FREE ports to edges and thus must not be normalized here.
func findPortSideRange(
	 *layered.LGraphArena,
	 []layered.LPortID,
	 layered.PortSide,
) (int, int) {
	if len() == 0 {
		return 0, 0
	}

	 := .Port([0]).Side
	 := 0
	 := 
	 :=  + 1
	for  < len()-1 &&  <  {
		++
		 = .Port([]).Side
	}
	 := 
	for  < len()-1 &&  <  {
		++
		 = .Port([]).Side
	}
	return , 
}