// Copyright (c) 2020 Kiel University and others.
//
// SPDX-License-Identifier: EPL-2.0

package preserveorder

import (
	basegraph 
	
)

// PortComparator ports ELK 0.12.0's ModelOrderPortComparator behavior used by
// D2's model-order profile. It retains the comparator's transitive ordering
// state because comparisons made earlier in the stable sort affect later ones.
type PortComparator struct {
	Arena                *layered.LGraphArena
	PreviousLayer        []layered.LNodeID
	Ordering             layered.OrderingStrategy
	TargetNodeModelOrder map[layered.LNodeID]int
	PortModelOrder       bool
	BiggerThan           map[layered.LPortID]map[layered.LPortID]struct{}
	SmallerThan          map[layered.LPortID]map[layered.LPortID]struct{}
}

func (
	 *layered.LGraphArena,
	 []layered.LNodeID,
	 layered.OrderingStrategy,
	 map[layered.LNodeID]int,
	 bool,
) *PortComparator {
	return &PortComparator{
		Arena:                ,
		PreviousLayer:        append([]layered.LNodeID(nil), ...),
		Ordering:             ,
		TargetNodeModelOrder: ,
		PortModelOrder:       ,
		BiggerThan:           make(map[layered.LPortID]map[layered.LPortID]struct{}),
		SmallerThan:          make(map[layered.LPortID]map[layered.LPortID]struct{}),
	}
}

func ( *PortComparator) (,  layered.LPortID) int {
	if  := .initializeAndRecall(, );  != 0 {
		return 
	}
	 := .Arena.Port()
	 := .Arena.Port()
	if .Side != .Side {
		 := compareInt(int(.Side), int(.Side))
		if  > 0 {
			.update(, , 1)
		} else {
			.update(, , 1)
		}
		return 
	}

	 := 1
	if len(.IncomingEdges) != 0 && len(.IncomingEdges) != 0 {
		if .Side == layered.WestSide || .Side == layered.NorthSide ||
			.Side == layered.SouthSide {
			 = -
		}
		 := *.Arena.Edge(.IncomingEdges[0]).Source
		 := *.Arena.Edge(.IncomingEdges[0]).Source
		 := *.Arena.Port().Node
		 := *.Arena.Port().Node
		if  ==  {
			for ,  := range .Arena.Node().Ports {
				if  ==  {
					.update(, , )
					return -
				}
				if  ==  {
					.update(, , )
					return 
				}
			}
		}
		 := .Arena.Node()
		 := .Arena.Node()
		 := .Arena.Node(*.Node)
		if .Type == layered.LongEdgeNode &&
			.Type == layered.LongEdgeNode &&
			.Layer != nil && .Layer != nil && .Layer != nil &&
			.Arena.Layer(*.Layer).ID == .Arena.Layer(*.Layer).ID &&
			.Arena.Layer(*.Layer).ID == .Arena.Layer(*.Layer).ID {
			 := .checkReferenceLayer(
				.Arena.Layer(*.Layer).Nodes,
				, ,
			)
			if  != 0 {
				if .Side == layered.EastSide {
					 = -
				}
				if  > 0 {
					.update(, , )
					return 
				}
				.update(, , )
				return -
			}
		}
		 := .checkReferenceLayer(
			.PreviousLayer, , )
		if  != 0 {
			if  > 0 {
				.update(, , )
				return 
			}
			.update(, , )
			return -
		}
		if .PortModelOrder {
			if  := .checkPortModelOrder(, );  != 0 {
				if  > 0 {
					.update(, , )
					return 
				}
				.update(, , )
				return -
			}
		}
	}

	if len(.OutgoingEdges) != 0 && len(.OutgoingEdges) != 0 {
		if .Side == layered.WestSide || .Side == layered.SouthSide {
			 = -
		}
		,  := basegraph.TryGet(
			&.Properties, layered.LongEdgeTargetNode)
		,  := basegraph.TryGet(
			&.Properties, layered.LongEdgeTargetNode)
		if .Ordering == layered.OrderingPreferNodes &&
			 &&  &&
			basegraph.Has(&.Arena.Node().Properties, layered.ModelOrder) &&
			basegraph.Has(&.Arena.Node().Properties, layered.ModelOrder) {
			 := .graph()
			 := basegraph.Get(&.Properties, layered.MaximumModelOrderNodes)
			 := CalculateModelOrderOrGroupModelOrder(
				,
				&.Arena.Node().Properties,
				&.Arena.Node().Properties,
				,
			)
			 := CalculateModelOrderOrGroupModelOrder(
				,
				&.Arena.Node().Properties,
				&.Arena.Node().Properties,
				,
			)
			if  >  {
				.update(, , )
				return 
			}
			.update(, , )
			return -
		}
		if .PortModelOrder {
			if  := .checkPortModelOrder(, );  != 0 {
				if  > 0 {
					.update(, , )
					return 
				}
				.update(, , )
				return -
			}
		}

		 := .Arena.Edge(.OutgoingEdges[0])
		 := .Arena.Edge(.OutgoingEdges[0])
		 := .graph()
		,  := 0, 0
		if basegraph.Has(&.Properties, layered.ModelOrder) {
			 = CalculateModelOrderOrGroupModelOrder(
				,
				&.Properties,
				&.Properties,
				len(.OutgoingEdges)+len(.IncomingEdges),
			)
		}
		if basegraph.Has(&.Properties, layered.ModelOrder) {
			 = CalculateModelOrderOrGroupModelOrder(
				,
				&.Properties,
				&.Properties,
				len(.OutgoingEdges)+len(.IncomingEdges),
			)
		}
		if  &&  &&  ==  {
			if  >  {
				.update(, , )
				return 
			}
			.update(, , )
			return -
		}
		if  {
			if ,  := .TargetNodeModelOrder[];  {
				 = 
			}
		}
		if  {
			if ,  := .TargetNodeModelOrder[];  {
				 = 
			}
		}
		if  >  {
			.update(, , )
			return 
		}
		.update(, , )
		return -
	}

	if len(.IncomingEdges) != 0 && len(.OutgoingEdges) != 0 {
		.update(, , )
		return 1
	}
	if len(.OutgoingEdges) != 0 && len(.IncomingEdges) != 0 {
		.update(, , )
		return -1
	}
	if basegraph.Has(&.Properties, layered.ModelOrder) &&
		basegraph.Has(&.Properties, layered.ModelOrder) {
		if .Side == layered.WestSide || .Side == layered.SouthSide {
			 = -
		}
		 := .graph()
		 := len(.Arena.Node(*.Node).Ports)
		 := CalculateModelOrderOrGroupModelOrder(
			, &.Properties, &.Properties, )
		 := CalculateModelOrderOrGroupModelOrder(
			, &.Properties, &.Properties, )
		if  >  {
			.update(, , )
			return 
		}
		.update(, , )
		return -
	}
	.update(, , )
	return -
}

func ( *PortComparator) (
	,  layered.LPortID,
) int {
	if ,  := .BiggerThan[]; ! {
		.BiggerThan[] = make(map[layered.LPortID]struct{})
	} else if containsPort(.BiggerThan[], ) {
		return 1
	}
	if ,  := .BiggerThan[]; ! {
		.BiggerThan[] = make(map[layered.LPortID]struct{})
	} else if containsPort(.BiggerThan[], ) {
		return -1
	}
	if ,  := .SmallerThan[]; ! {
		.SmallerThan[] = make(map[layered.LPortID]struct{})
	} else if containsPort(.SmallerThan[], ) {
		return -1
	}
	if ,  := .SmallerThan[]; ! {
		.SmallerThan[] = make(map[layered.LPortID]struct{})
	} else if containsPort(.BiggerThan[], ) {
		// Preserve the source comparator's second biggerThan lookup.
		return 1
	}
	return 0
}

func ( *PortComparator) (
	,  layered.LPortID,
) int {
	 := .Arena.Port()
	 := .Arena.Port()
	if basegraph.Has(&.Properties, layered.ModelOrder) &&
		basegraph.Has(&.Properties, layered.ModelOrder) {
		 := .graph()
		 := len(.Arena.Node(*.Node).Ports)
		return compareInt(
			CalculateModelOrderOrGroupModelOrder(
				, &.Properties, &.Properties, ),
			CalculateModelOrderOrGroupModelOrder(
				, &.Properties, &.Properties, ),
		)
	}
	return 0
}

func ( *PortComparator) ( layered.LPortID) *layered.LGraph {
	 := *.Arena.Port().Node
	return .Arena.Graph(.Arena.NodeGraph())
}

func ( *PortComparator) (
	,  layered.LPortID,
	 int,
) {
	,  := , 
	if  < 0 {
		,  = , 
	}
	.ensureOrderingEntry()
	.ensureOrderingEntry()
	 := .BiggerThan[]
	 := .BiggerThan[]
	 := .SmallerThan[]
	 := .SmallerThan[]
	[] = struct{}{}
	[] = struct{}{}
	for  := range  {
		.ensureOrderingEntry()
		[] = struct{}{}
		.SmallerThan[][] = struct{}{}
		for  := range  {
			.SmallerThan[][] = struct{}{}
		}
	}
	for  := range  {
		.ensureOrderingEntry()
		[] = struct{}{}
		.BiggerThan[][] = struct{}{}
		for  := range  {
			.BiggerThan[][] = struct{}{}
		}
	}
}

func ( *PortComparator) ( layered.LPortID) {
	if .BiggerThan[] == nil {
		.BiggerThan[] = make(map[layered.LPortID]struct{})
	}
	if .SmallerThan[] == nil {
		.SmallerThan[] = make(map[layered.LPortID]struct{})
	}
}

func ( *PortComparator) (
	 []layered.LNodeID,
	,  layered.LNodeID,
) int {
	for ,  := range  {
		if  ==  {
			return -1
		}
		if  ==  {
			return 1
		}
	}
	return 0
}

func ( *PortComparator) () {
	.BiggerThan = make(map[layered.LPortID]map[layered.LPortID]struct{})
	.SmallerThan = make(map[layered.LPortID]map[layered.LPortID]struct{})
}

func containsPort( map[layered.LPortID]struct{},  layered.LPortID) bool {
	,  := []
	return 
}