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

import (
	

	
	
	
	basegraph 
)

// ImportMaps preserve object identity between ELK's public graph arena and
// its layered working graph.
type ImportMaps struct {
	Nodes  map[basegraph.NodeID]LNodeID
	Ports  map[basegraph.PortID]LPortID
	Edges  map[basegraph.EdgeID]LEdgeID
	Labels map[basegraph.LabelID]LLabelID
	Graphs map[basegraph.NodeID]LGraphID
}

type ElkGraphImporter struct {
	Model *basegraph.Model
	Maps  ImportMaps
}

func ( *basegraph.Model) *ElkGraphImporter {
	return &ElkGraphImporter{Model: , Maps: ImportMaps{
		Nodes: make(map[basegraph.NodeID]LNodeID), Ports: make(map[basegraph.PortID]LPortID),
		Edges: make(map[basegraph.EdgeID]LEdgeID), Labels: make(map[basegraph.LabelID]LLabelID),
		Graphs: make(map[basegraph.NodeID]LGraphID),
	}}
}

func ( *ElkGraphImporter) ( basegraph.NodeID) (*LGraphArena, LGraphID, error) {
	if .Model == nil {
		return nil, 0, fmt.Errorf("layered: cannot import a nil graph")
	}
	 := NewLGraphArena()
	 := .createGraph(, )
	.Maps.Graphs[] = 
	if basegraph.Get(&.Graph().Properties, core.PartitioningActivate) {
		.addGraphProperty(, , GraphPropertyPartitions)
	}
	if  := .importExternalPorts(, , );  != nil {
		return nil, 0, 
	}

	 := baseHierarchy(.Model.Node()) == HierarchyIncludeChildren
	if  := .importNodes(, , , );  != nil {
		return nil, 0, 
	}
	if  := .importEdges(, , , );  != nil {
		return nil, 0, 
	}
	if  := .importInsideSelfLoops(, , );  != nil {
		return nil, 0, 
	}
	if ,  := basegraph.TryGet(&.Graph().Properties, SpacingBaseValue);  {
		ApplySpacingBaseValue(&.Graph().Properties, )
	}
	return , , nil
}

// importExternalPorts mirrors ElkGraphImporter's top-level external-port
// transformation. A recursive layout of a compound node represents the
// compound node's own ports as boundary dummies in the graph being laid out.
func ( *ElkGraphImporter) (
	 *LGraphArena,
	 basegraph.NodeID,
	 LGraphID,
) error {
	 := .Model.Node()
	if len(.Ports) == 0 {
		return nil
	}
	for ,  := range .Ports {
		.ensureDefinedExternalPortSide(, , )
	}

	,  := false, false
	 := stringSetContains(
		basegraph.Get(&.Properties, nodespacing.PortLabelsPlacement), "INSIDE")
	for ,  := range .Ports {
		 := .externalPortEdgeCount(, )
		if  > 0 ||  && len(.Model.Port().Labels) > 0 {
			 = true
		}
		if  > 1 {
			 = true
		}
		if  &&  {
			break
		}
	}
	if ! {
		return nil
	}
	.addGraphProperty(, , GraphPropertyExternalPorts)
	if  {
		.addGraphProperty(, , GraphPropertyHyperedges)
	}
	for ,  := range .Ports {
		if  := .transformExternalPort(, , , );  != nil {
			return 
		}
	}
	return nil
}

func ( *ElkGraphImporter) (
	 *LGraphArena,
	 LGraphID,
	 basegraph.PortID,
) {
	 := .Model.Port()
	 := basegraph.Get(&.Graph().Properties, DirectionOption)
	 := basegraph.Get(&.Graph().Properties, PortConstraintsOption)
	 := basegraph.Get(&.Properties, PortSideOption)
	if !.IsSideFixed() {
		 = DirectionPortSide()
		if .calculateExternalPortNetFlow() <= 0 {
			 = .Opposed()
		}
	} else if  == UndefinedSide {
		 := .Model.Node(.Parent)
		 = inferPortSide(
			geometry.Point{X: .Shape.Width, Y: .Shape.Height},
			.Shape.Position(),
			geometry.Point{X: .Shape.Width, Y: .Shape.Height},
			,
		)
		if  == UndefinedSide {
			 = DirectionPortSide()
		}
	}
	basegraph.Set(&.Properties, PortSideOption, )
}

func ( *ElkGraphImporter) (
	 basegraph.NodeID,
	 basegraph.PortID,
) int {
	 := .Model.Port()
	 := 0
	 := make(map[basegraph.EdgeID]bool)
	 := func( basegraph.EdgeID) {
		if [] {
			return
		}
		[] = true
		 := .Model.Edge()
		if .isInsideSelfLoop(, ) {
			++
			return
		}
		if len(.Sources) == 0 || len(.Targets) == 0 {
			return
		}
		var  basegraph.NodeID
		if shapeListContainsPort(.Sources, ) {
			 = .Model.ShapeNode(.Targets[0])
		} else {
			 = .Model.ShapeNode(.Sources[0])
		}
		if  := .Model.Node(); .Parent != nil && *.Parent ==  {
			++
		}
	}
	for ,  := range .OutgoingEdges {
		()
	}
	for ,  := range .IncomingEdges {
		()
	}
	return 
}

func ( *ElkGraphImporter) (
	 *LGraphArena,
	 basegraph.NodeID,
	 basegraph.PortID,
	 LGraphID,
) error {
	 := .Model.Port()
	 := .Model.Node()
	 := geometry.Point{X: .Shape.Width, Y: .Shape.Height}
	 := .Shape.Position().Add(.Scale(.5))
	 := basegraph.Get(&.Graph().Properties, PortConstraintsOption)
	if  == PortConstraintsUndefined {
		 = PortConstraintsFree
	}
	 := basegraph.Get(&.Properties, PortSideOption)
	if  == UndefinedSide {
		return fmt.Errorf("layered: external port %s has no defined side", .Identifier)
	}
	if !basegraph.Has(&.Properties, PortBorderOffset) {
		 := 0.0
		if .Shape.X != 0 || .Shape.Y != 0 {
			 = externalPortBorderOffset(, , )
		}
		basegraph.Set(&.Properties, PortBorderOffset, )
	}

	 := geometry.Point{X: .Shape.Width, Y: .Shape.Height}
	 := .CreateExternalPortDummy(
		&.Properties,
		,
		,
		.calculateExternalPortNetFlow(),
		&,
		&,
		,
		basegraph.Get(&.Graph().Properties, DirectionOption),
		,
	)
	basegraph.Set(&.Node().Properties, OriginalElement, PortOrigin())
	basegraph.Set(&.Node().Properties, nodespacing.PortLabelsPlacement, []string{"OUTSIDE"})
	 := .Node().Ports[0]
	.Port().ConnectedToExternalNodes = .isConnectedToExternalNodes()

	 := stringSetContains(
		basegraph.Get(&.Properties, nodespacing.PortLabelsPlacement), "INSIDE")
	 := fixedPortLabelPlacement(&.Properties)
	for ,  := range .Labels {
		 := .Model.Label()
		if .Text == "" || basegraph.Get(&.Properties, core.NoLayout) {
			continue
		}
		 := .transformLabel(, )
		.Port().Labels = append(.Port().Labels, )
		if ! {
			 := 0.0
			if  {
				 = portLabelInsidePart(, , )
			}
			switch  {
			case EastSide, WestSide:
				.Label().Size.X = 
			case NorthSide, SouthSide:
				.Label().Size.Y = 
			}
		}
	}

	 := &basegraph.PropertyMap{}
	if .Parent != nil {
		 = &.Model.Node(*.Parent).Properties
	}
	basegraph.Set(&.Node().Properties, SpacingLabelPortHorizontal,
		basegraph.Get(, SpacingLabelPortHorizontal))
	basegraph.Set(&.Node().Properties, SpacingLabelPortVertical,
		basegraph.Get(, SpacingLabelPortVertical))
	basegraph.Set(&.Node().Properties, SpacingLabelLabel,
		basegraph.Get(, SpacingLabelLabel))
	.Graph().LayerlessNodes = append(.Graph().LayerlessNodes, )
	.Maps.Ports[] = 
	return nil
}

func ( *ElkGraphImporter) ( basegraph.PortID) int {
	 := .Model.Port()
	 := .Parent
	,  := 0, 0
	for ,  := range .OutgoingEdges {
		 := .Model.Edge()
		if len(.Targets) == 0 {
			continue
		}
		 := core.IsSelfLoop(.Model, )
		 := .isInsideSelfLoop(, )
		 := .Model.ShapeNode(.Targets[0])
		switch {
		case  && :
			++
		case :
			++
		case .Model.Node().Parent != nil && *.Model.Node().Parent ==  ||  == :
			++
		default:
			++
		}
	}
	for ,  := range .IncomingEdges {
		 := .Model.Edge()
		if len(.Sources) == 0 {
			continue
		}
		 := core.IsSelfLoop(.Model, )
		 := .isInsideSelfLoop(, )
		 := .Model.ShapeNode(.Sources[0])
		switch {
		case  && :
			++
		case :
			++
		case .Model.Node().Parent != nil && *.Model.Node().Parent ==  ||  == :
			++
		default:
			++
		}
	}
	return  - 
}

func ( *ElkGraphImporter) ( basegraph.PortID) bool {
	 := .Model.Port()
	 := .Parent
	for ,  := range .OutgoingEdges {
		 := .Model.Edge()
		if len(.Targets) > 0 && !.Model.IsDescendant(.Model.ShapeNode(.Targets[0]), ) {
			return true
		}
	}
	for ,  := range .IncomingEdges {
		 := .Model.Edge()
		if len(.Sources) > 0 && !.Model.IsDescendant(.Model.ShapeNode(.Sources[0]), ) {
			return true
		}
	}
	return false
}

func externalPortBorderOffset( *basegraph.Node,  *basegraph.Port,  PortSide) float64 {
	switch  {
	case NorthSide:
		return -(.Shape.Y + .Shape.Height)
	case EastSide:
		return .Shape.X - .Shape.Width
	case SouthSide:
		return .Shape.Y - .Shape.Height
	case WestSide:
		return -(.Shape.X + .Shape.Width)
	default:
		return 0
	}
}

func portLabelInsidePart( *basegraph.Label,  *basegraph.Port,  PortSide) float64 {
	switch  {
	case NorthSide:
		return max(0, .Shape.Height+.Shape.Y-.Shape.Height)
	case SouthSide:
		return max(0, -.Shape.Y)
	case EastSide:
		return max(0, -.Shape.X)
	case WestSide:
		return max(0, .Shape.Width+.Shape.X-.Shape.Width)
	default:
		return 0
	}
}

func shapeListContainsPort( []basegraph.ShapeID,  basegraph.PortID) bool {
	for ,  := range  {
		if .Kind == basegraph.PortShape && .Port ==  {
			return true
		}
	}
	return false
}

func stringSetContains( []string,  string) bool {
	for ,  := range  {
		if  ==  {
			return true
		}
	}
	return false
}

func ( *ElkGraphImporter) ( *LGraphArena,  basegraph.NodeID) LGraphID {
	 := .AddGraph()
	 := .Graph()
	copyTypedProperties(&.Properties, &.Model.Node().Properties)
	basegraph.Set(&.Properties, OriginalElement, NodeOrigin())
	basegraph.Set(&.Properties, GraphProperties, GraphProperty(0))
	.Padding = basegraph.Get(&.Properties, Padding)
	if basegraph.Get(&.Properties, DirectionOption) == DirectionUndefined {
		basegraph.Set(&.Properties, DirectionOption, DirectionRight)
	}
	return 
}

func ( *ElkGraphImporter) (
	 *LGraphArena,
	 basegraph.NodeID,
	 LGraphID,
	 bool,
) error {
	if ! {
		 := 0
		for ,  := range .Model.Node().Children {
			if basegraph.Get(&.Model.Node().Properties, core.NoLayout) {
				continue
			}
			 := .transformNode(, , )
			if needsModelOrder(.Graph()) &&
				!basegraph.Get(&.Model.Node().Properties, NoModelOrder) {
				basegraph.Set(&.Node().Properties, ModelOrder, )
				++
			}
		}
		basegraph.Set(&.Graph().Properties, MaximumModelOrderNodes, )
		return nil
	}

	 := basegraph.Get(&.Graph().Properties, DirectionOption)
	 := append([]basegraph.NodeID(nil), .Model.Node().Children...)
	 := 0
	for len() > 0 {
		 := [0]
		 = [1:]
		 := .Model.Node()
		if basegraph.Get(&.Properties, core.NoLayout) {
			continue
		}
		 := 
		if .Parent != nil && *.Parent !=  {
			,  := .Maps.Nodes[*.Parent]
			if ! || .Node().NestedGraph == nil {
				return fmt.Errorf("layered: parent of node %s has no nested graph", .Identifier)
			}
			 = *.Node().NestedGraph
		}

		var  *LGraphID
		if len(.Children) > 0 && baseHierarchy() == HierarchyIncludeChildren {
			 := .createGraph(, )
			basegraph.Set(&.Graph().Properties, DirectionOption, )
			.Maps.Graphs[] = 
			 = &
			if ,  := basegraph.TryGet(&.Graph().Properties, SpacingBaseValue);  {
				ApplySpacingBaseValue(&.Graph().Properties, )
			}
		}

		 := .transformNode(, , )
		if needsModelOrder(.Graph()) &&
			!basegraph.Get(&.Properties, NoModelOrder) {
			basegraph.Set(&.Node().Properties, ModelOrder, )
			++
		}
		if  != nil {
			.Node().NestedGraph = 
			.Graph(*).ParentNode = &
			 = append(, .Children...)
		}
	}
	basegraph.Set(&.Graph().Properties, MaximumModelOrderNodes, )
	return nil
}

func ( *ElkGraphImporter) (
	 *LGraphArena,
	 basegraph.NodeID,
	 LGraphID,
) LNodeID {
	 := .Model.Node()
	 := .AddNode()
	 := .Node()
	copyTypedProperties(&.Properties, &.Properties)
	copyIndividualSpacings(, )
	basegraph.Set(&.Properties, OriginalElement, NodeOrigin())
	.Position = .Shape.Position()
	.Size = geometry.Point{X: .Shape.Width, Y: .Shape.Height}
	.Margin = basegraph.Get(&.Properties, nodespacing.Margins)
	.Graph().LayerlessNodes = append(.Graph().LayerlessNodes, )
	.Maps.Nodes[] = 

	if len(.Children) > 0 || basegraph.Get(&.Properties, core.InsideSelfLoopsActivate) {
		basegraph.Set(&.Properties, CompoundNode, true)
	}
	 := basegraph.Get(&.Properties, PortConstraintsOption)
	if  == PortConstraintsUndefined {
		 = PortConstraintsFree
		basegraph.Set(&.Properties, PortConstraintsOption, )
	} else if  != PortConstraintsFree {
		.addGraphProperty(, , GraphPropertyNonFreePorts)
	}
	 := basegraph.Get(&.Graph().Properties, DirectionOption)
	 := needsModelOrder(.Graph()) &&
		!basegraph.Get(&.Properties, NoModelOrder)
	for ,  := range .Ports {
		if basegraph.Get(&.Model.Port().Properties, core.NoLayout) {
			continue
		}
		 := .transformPort(, , , , , )
		if  {
			basegraph.Set(&.Port().Properties, ModelOrder, )
		}
	}
	for ,  := range .Labels {
		 := .Model.Label()
		if .Text == "" || basegraph.Get(&.Properties, core.NoLayout) {
			continue
		}
		.Labels = append(.Labels, .transformLabel(, ))
	}
	if basegraph.Get(&.Properties, core.CommentBox) {
		.addGraphProperty(, , GraphPropertyComments)
	}
	if basegraph.Get(&.Properties, core.Hypernode) {
		.addGraphProperty(, , GraphPropertyHypernodes)
		.addGraphProperty(, , GraphPropertyHyperedges)
		basegraph.Set(&.Properties, PortConstraintsOption, PortConstraintsFree)
	}
	return 
}

// LayoutConfigurator stores parent-targeted spacing options configured on a
// node in CoreOptions.SPACING_INDIVIDUAL. Direct spacing properties on the
// node are not individual overrides; they participate in normal option
// inheritance and must not be wrapped here.
func copyIndividualSpacings( *LNode,  *basegraph.Node) {
	var  IndividualSpacings
	if ,  := .Properties.Raw(core.SpacingIndividual.ID);  {
		switch typed := .(type) {
		case *core.IndividualSpacings:
			if  != nil {
				.Properties.CopyFrom(&.Properties)
			}
		case core.IndividualSpacings:
			.Properties.CopyFrom(&.Properties)
		case IndividualSpacings:
			.Properties.CopyFrom(&.Properties)
		}
	}
	if !.Properties.Empty() {
		basegraph.Set(&.Properties, SpacingIndividual, )
	}
}

func ( *ElkGraphImporter) (
	 *LGraphArena,
	 basegraph.PortID,
	 LNodeID,
	 LGraphID,
	 Direction,
	 PortConstraints,
) LPortID {
	 := .Model.Port()
	 := .AddPort()
	 := .Port()
	copyTypedProperties(&.Properties, &.Properties)
	basegraph.Set(&.Properties, OriginalElement, PortOrigin())
	.Position = .Shape.Position()
	.Size = geometry.Point{X: .Shape.Width, Y: .Shape.Height}
	.SetPortNode(, &)
	 := basegraph.Get(&.Properties, PortSideOption)
	if  == UndefinedSide {
		if .IsSideFixed() {
			 = inferPortSide(.Node().Size, .Position, .Size, )
		} else if len(.IncomingEdges)-len(.OutgoingEdges) > 0 {
			 = DirectionPortSide()
		} else {
			 = DirectionPortSide().Opposed()
		}
	}
	.SetPortSide(, )
	for ,  := range .Labels {
		 := .Model.Label()
		if .Text == "" || basegraph.Get(&.Properties, core.NoLayout) {
			continue
		}
		.Labels = append(.Labels, .transformLabel(, ))
	}
	if ( == DirectionRight ||  == DirectionLeft) && ( == NorthSide ||  == SouthSide) ||
		( == DirectionUp ||  == DirectionDown) && ( == EastSide ||  == WestSide) {
		.addGraphProperty(, , GraphPropertyNorthSouthPorts)
	}
	.Maps.Ports[] = 
	return 
}

func ( *ElkGraphImporter) ( *LGraphArena,  basegraph.LabelID) LLabelID {
	 := .Model.Label()
	 := .AddLabel(.Text)
	 := .Label()
	copyTypedProperties(&.Properties, &.Properties)
	basegraph.Set(&.Properties, OriginalElement, LabelOrigin())
	.Position = .Shape.Position()
	.Size = geometry.Point{X: .Shape.Width, Y: .Shape.Height}
	.Maps.Labels[] = 
	return 
}

func ( *ElkGraphImporter) (
	 *LGraphArena,
	 basegraph.NodeID,
	 LGraphID,
	 bool,
) error {
	 := []basegraph.NodeID{}
	if  {
		// importHierarchicalGraph visits contained-edge lists breadth first,
		// independently of the order in the global edge arena. This is visible
		// through MODEL_ORDER when a later JSON edge is contained by a shallower
		// graph than an earlier one.
		for  := 0;  < len(); ++ {
			for ,  := range .Model.Node([]).Children {
				if ,  := .Maps.Graphs[];  {
					 = append(, )
				}
			}
		}
	}

	 := 0
	for ,  := range  {
		for ,  := range append([]basegraph.EdgeID(nil), .Model.Node().ContainedEdges...) {
			 := .Model.Edge()
			if needsModelOrder(.Graph()) {
				basegraph.Set(&.Properties, ModelOrder, )
				++
			}
			if len(.Sources) != 1 || len(.Targets) != 1 {
				return fmt.Errorf("layered: edge %s is a hyperedge; hyperedges are unsupported", .Identifier)
			}
			if basegraph.Get(&.Properties, core.NoLayout) {
				continue
			}
			// Inside self loops are laid out recursively in the source node's
			// own graph, not in the graph that contains the source node.
			if .isInsideSelfLoop(.Model.ShapeNode(.Sources[0]), ) {
				continue
			}
			 := .Model.ShapeNode(.Sources[0])
			 := .Model.ShapeNode(.Targets[0])
			if ,  := .mappedEndpointNode(, .Sources[0]); ! {
				continue
			}
			if ,  := .mappedEndpointNode(, .Targets[0]); ! {
				continue
			}
			 := .edgeGraph(, , , , , )
			,  := .transformEdge(, , )
			if  != nil {
				return 
			}
			if needsModelOrder(.Graph()) {
				basegraph.Set(&.Edge().Properties, ModelOrder,
					basegraph.Get(&.Properties, ModelOrder))
			}
			if  !=  && .Model.Node().Parent != .Model.Node().Parent {
				if ,  := .Maps.Graphs[];  {
					basegraph.Set(&.Edge().Properties, CoordinateSystemOrigin, )
				}
			}
		}
	}
	return nil
}

func ( *ElkGraphImporter) (
	 *LGraphArena,
	 basegraph.NodeID,
	 LGraphID,
) error {
	 := .Model.Node()
	if .Parent == nil {
		return nil
	}
	for ,  := range .Model.Node(*.Parent).ContainedEdges {
		if !.isInsideSelfLoop(, ) {
			continue
		}
		if ,  := .Maps.Edges[];  {
			continue
		}
		if ,  := .transformEdge(, , );  != nil {
			return 
		}
	}
	return nil
}

func ( *ElkGraphImporter) (
	 basegraph.NodeID,
	 basegraph.EdgeID,
) bool {
	 := .Model.Edge()
	return basegraph.Get(&.Model.Node().Properties, core.InsideSelfLoopsActivate) &&
		basegraph.Get(&.Properties, core.InsideSelfLoopsEdge) &&
		core.IsSelfLoop(.Model, ) &&
		len(.Sources) > 0 && .Model.ShapeNode(.Sources[0]) == 
}

func ( *ElkGraphImporter) (
	 *LGraphArena,
	 LGraphID,
	, , ,  basegraph.NodeID,
) LGraphID {
	if .Model.IsDescendant(, ) {
		if ,  := .Maps.Graphs[];  {
			return 
		}
	}
	if .Model.IsDescendant(, ) {
		if ,  := .Maps.Graphs[];  {
			return 
		}
	}
	if  ==  {
		return 
	}
	if ,  := .Maps.Graphs[];  {
		return 
	}
	if ,  := .Maps.Nodes[];  {
		return .NodeGraph()
	}
	return 
}

func ( *ElkGraphImporter) (
	 *LGraphArena,
	 basegraph.EdgeID,
	 LGraphID,
) (LEdgeID, error) {
	 := .Model.Edge()
	if len(.Sources) != 1 || len(.Targets) != 1 {
		return 0, fmt.Errorf("layered: edge %s is a hyperedge; hyperedges are unsupported", .Identifier)
	}
	 := .Model.ShapeNode(.Sources[0])
	 := .Model.ShapeNode(.Targets[0])
	,  := .mappedEndpointNode(, .Sources[0])
	,  := .mappedEndpointNode(, .Targets[0])
	if ! || ! {
		return 0, fmt.Errorf("layered: source or target of edge %s could not be transformed", .Identifier)
	}
	,  := .mappedPort(.Sources[0])
	if ! {
		 = .createDefaultPort(, , , PortTypeOutput)
	}
	,  := .mappedPort(.Targets[0])
	if ! {
		 = .createDefaultPort(, .NodeGraph(), , PortTypeInput)
	}

	 := .AddEdge()
	 := .Edge()
	copyTypedProperties(&.Properties, &.Properties)
	basegraph.Set(&.Properties, OriginalElement, EdgeOrigin())
	basegraph.Unset(&.Properties, JunctionPoints)
	.SetEdgeSource(, &)
	.SetEdgeTarget(, &)
	.Maps.Edges[] = 

	if  ==  {
		.addGraphProperty(, , GraphPropertySelfLoops)
	}
	if len(.Port().IncomingEdges) > 1 || len(.Port().OutgoingEdges) > 1 ||
		len(.Port().IncomingEdges) > 1 || len(.Port().OutgoingEdges) > 1 {
		.addGraphProperty(, , GraphPropertyHyperedges)
	}
	for ,  := range .Labels {
		 := .Model.Label()
		if .Text == "" || basegraph.Get(&.Properties, core.NoLayout) {
			continue
		}
		 := .transformLabel(, )
		.Labels = append(.Labels, )
		switch basegraph.Get(&.Label().Properties, EdgeLabelsPlacement) {
		case EdgeLabelPlacementHead, EdgeLabelPlacementTail:
			.addGraphProperty(, , GraphPropertyEndLabels)
		case EdgeLabelPlacementCenter:
			.addGraphProperty(, , GraphPropertyCenterLabels)
		}
	}
	if (basegraph.Get(&.Graph().Properties, CrossingMinimization) == CrossingMinimizationInteractive ||
		basegraph.Get(&.Graph().Properties, NodePlacement) == NodePlacementInteractive) &&
		len(.Sections) > 0 {
		 := .Model.Section(.Sections[0])
		if len(.BendPoints) > 0 {
			basegraph.Set(&.Properties, OriginalBendPoints, .BendPoints.Clone())
		}
	}
	return , nil
}

func ( *ElkGraphImporter) (
	 *LGraphArena,
	 basegraph.ShapeID,
) (LNodeID, bool) {
	if .Kind == basegraph.PortShape {
		if ,  := .Maps.Ports[.Port];  && .Port().Node != nil {
			return *.Port().Node, true
		}
	}
	,  := .Maps.Nodes[.Model.ShapeNode()]
	return , 
}

func ( *ElkGraphImporter) ( basegraph.ShapeID) (LPortID, bool) {
	if .Kind != basegraph.PortShape {
		return 0, false
	}
	,  := .Maps.Ports[.Port]
	return , 
}

func ( *ElkGraphImporter) (
	 *LGraphArena,
	 LGraphID,
	 LNodeID,
	 PortType,
) LPortID {
	 := .AddPort()
	.SetPortNode(, &)
	 := basegraph.Get(&.Graph().Properties, DirectionOption)
	 := DirectionPortSide()
	if  == PortTypeInput {
		 = .Opposed()
	}
	.SetPortSide(, )
	return 
}

func ( *ElkGraphImporter) ( *LGraphArena,  LGraphID,  GraphProperty) {
	 := basegraph.Get(&.Graph().Properties, GraphProperties)
	basegraph.Set(&.Graph().Properties, GraphProperties, |)
}

func needsModelOrder( *LGraph) bool {
	 := basegraph.Get(&.Properties, CycleBreaking)
	 := basegraph.Get(&.Properties, Layering)
	 := basegraph.Get(&.Properties, NodePromotionStrategyOption)
	return basegraph.Get(&.Properties, ConsiderModelOrder) != OrderingNone ||
		 == CycleBreakingModelOrder ||  == CycleBreakingGreedyModelOrder ||
		 == CycleBreakingSCCConnectivity ||  == CycleBreakingSCCNodeType ||
		 == CycleBreakingDFSNodeOrder ||  == CycleBreakingBFSNodeOrder ||
		 == LayeringBreadthFirstModelOrder ||  == LayeringDepthFirstModelOrder ||
		 == NodePromotionModelOrderLeftToRight ||
		 == NodePromotionModelOrderRightToLeft ||
		basegraph.Get(&.Properties, ForceNodeModelOrder) ||
		basegraph.Get(&.Properties, ComponentOrdering) != ComponentOrderingNone ||
		basegraph.Get(&.Properties, CrossingCounterNodeInfluence) != 0 ||
		basegraph.Get(&.Properties, CrossingCounterPortInfluence) != 0
}

func baseHierarchy( *basegraph.Node) HierarchyHandling {
	,  := .Properties.Raw(core.HierarchyHandling.ID)
	if ,  := parseHierarchyHandling(fmt.Sprint());  {
		return 
	}
	return HierarchySeparateChildren
}

func inferPortSide(, ,  geometry.Point,  Direction) PortSide {
	 := .Add(.Scale(0.5))
	 := []struct {
		  PortSide
		 float64
	}{
		{NorthSide, .Y},
		{EastSide, .X - .X},
		{SouthSide, .Y - .Y},
		{WestSide, .X},
	}
	 := [0]
	for ,  := range [1:] {
		if . < . {
			 = 
		}
	}
	if . < 0 {
		return DirectionPortSide()
	}
	return .
}