// Copyright (c) 2020 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 
	
	
	
)

const longEdgeOrderingOptionID = "org.eclipse.elk.layered.considerModelOrder.longEdgeStrategy"

// SortByInputModel ports ELK 0.8.1's SortByInputModelProcessor. It must run
// after long-edge splitting and before north/south port dummy insertion.
func ( *layered.LGraphArena,  layered.LGraphID) error {
	 := .Graph()
	 := basegraph.Get(&.Properties, layered.ConsiderModelOrder)
	 := graphLongEdgeOrdering()
	for ,  := range .Layers {
		.Layer().ID = 
		 := 
		if  != 0 {
			--
		}
		 := .Layers[]
		 := preserveorder.NewNodeComparator(
			, .Layer().Nodes, , , true)
		insertionSortNodes(.Layer().Nodes, .Compare)
		for ,  := range .Layer().Nodes {
			 := .Node()
			 := basegraph.Get(&.Properties, layered.PortConstraintsOption)
			if  == layered.PortConstraintsFixedOrder ||  == layered.PortConstraintsFixedPosition {
				continue
			}
			,  := LongEdgeTargetNodePreprocessing(, )
			if  != nil {
				return 
			}
			 := preserveorder.NewPortComparator(
				,
				.Layer().Nodes,
				,
				,
				basegraph.Get(&.Properties, layered.PortModelOrder),
			)
			javacompat.GWTStableSort(.Ports, .Compare)
			.InvalidatePortSideCache()
		}
		 = preserveorder.NewNodeComparator(
			, .Layer().Nodes, , , false)
		insertionSortNodes(.Layer().Nodes, .Compare)
	}
	return nil
}

func insertionSortNodes(
	 []layered.LNodeID,
	 func(layered.LNodeID, layered.LNodeID) int,
) {
	for  := 1;  < len(); ++ {
		 := []
		 := 
		for  > 0 && ([-1], ) > 0 {
			[] = [-1]
			--
		}
		[] = 
	}
}

// LongEdgeTargetNodePreprocessing calculates each outgoing port's ultimate
// target and caches the minimum non-reversed edge order per target node.
func (
	 *layered.LGraphArena,
	 layered.LNodeID,
) (map[layered.LNodeID]int, error) {
	 := .Node()
	if ,  := basegraph.TryGet(&.Properties, layered.TargetNodeModelOrder);  {
		return , nil
	}
	 := make(map[layered.LNodeID]int)
	for ,  := range .Ports {
		 := .Port()
		if len(.OutgoingEdges) == 0 {
			continue
		}
		, ,  := LongEdgeTargetNode(, )
		if  != nil {
			return nil, 
		}
		if  {
			basegraph.Set(&.Properties, layered.LongEdgeTargetNode, )
		} else {
			basegraph.Unset(&.Properties, layered.LongEdgeTargetNode)
			continue
		}
		 := .Edge(.OutgoingEdges[0])
		if basegraph.Get(&.Properties, layered.Reversed) {
			continue
		}
		 := basegraph.Get(&.Properties, layered.ModelOrder)
		if ,  := []; ! ||  <  {
			[] = 
		}
	}
	basegraph.Set(&.Properties, layered.TargetNodeModelOrder, )
	return , nil
}

// LongEdgeTargetNode follows a proper long-edge dummy chain to its normal
// target. A dummy's LONG_EDGE_TARGET shortcut takes precedence, exactly as in
// SortByInputModelProcessor.getTargetNode.
func (
	 *layered.LGraphArena,
	 layered.LPortID,
) (layered.LNodeID, bool, error) {
	 := .Port().OutgoingEdges
	if len() == 0 {
		return 0, false, fmt.Errorf("model-order target lookup requires an outgoing edge")
	}
	 := [0]
	for {
		 := .Edge()
		if .Target == nil || .Port(*.Target).Node == nil {
			return 0, false, fmt.Errorf("model-order target lookup encountered a detached edge")
		}
		 := *.Port(*.Target).Node
		 := .Node()
		if ,  := basegraph.TryGet(&.Properties, layered.LongEdgeTarget);  {
			if .Port().Node == nil {
				return 0, false, nil
			}
			return *.Port().Node, true, nil
		}
		if .Type == layered.NormalNode {
			return , true, nil
		}
		 := .NodeOutgoingEdges()
		if len() == 0 {
			return 0, false, nil
		}
		 = [0]
	}
}

func graphLongEdgeOrdering( *layered.LGraph) preserveorder.LongEdgeOrderingStrategy {
	,  := .Properties.Raw(longEdgeOrderingOptionID)
	if ! {
		return preserveorder.DummyNodeOver
	}
	if ,  := .(preserveorder.LongEdgeOrderingStrategy);  {
		return 
	}
	switch strings.ToUpper(fmt.Sprint()) {
	case "DUMMY_NODE_UNDER":
		return preserveorder.DummyNodeUnder
	case "EQUAL":
		return preserveorder.LongEdgeEqual
	default:
		return preserveorder.DummyNodeOver
	}
}