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

package layered

import (
	
	basegraph 
)

// ReverseEdge mirrors LEdge.reverse. Reversal updates adjacency in the same
// sequence as Java, toggles REVERSED, reverses bend points, and swaps HEAD and
// TAIL label placement. Collector-port adaptation is used by cycle breaking.
func ( *LGraphArena) ( LGraphID,  LEdgeID,  bool) {
	 := .Edge()
	,  := *.Source, *.Target
	.SetEdgeSource(, nil)
	.SetEdgeTarget(, nil)

	 := 
	if  && basegraph.Get(&.Port().Properties, InputCollect) {
		 = .ProvideCollectorPort(, *.Port().Node, PortTypeOutput, EastSide)
	}
	 := 
	if  && basegraph.Get(&.Port().Properties, OutputCollect) {
		 = .ProvideCollectorPort(, *.Port().Node, PortTypeInput, WestSide)
	}
	.SetEdgeSource(, &)
	.SetEdgeTarget(, &)

	for ,  := range .Edge().Labels {
		 := basegraph.Get(&.Label().Properties, EdgeLabelsPlacement)
		switch  {
		case EdgeLabelPlacementHead:
			basegraph.Set(&.Label().Properties, EdgeLabelsPlacement, EdgeLabelPlacementTail)
		case EdgeLabelPlacementTail:
			basegraph.Set(&.Label().Properties, EdgeLabelsPlacement, EdgeLabelPlacementHead)
		}
	}
	basegraph.Set(&.Edge().Properties, Reversed,
		!basegraph.Get(&.Edge().Properties, Reversed))
	.Edge().BendPoints = .Edge().BendPoints.Reverse()
}

func ( *LGraphArena) (
	 LGraphID,
	 LNodeID,
	 PortType,
	 PortSide,
) LPortID {
	 := InputCollect
	if  == PortTypeOutput {
		 = OutputCollect
	}
	for ,  := range .Node().Ports {
		if basegraph.Get(&.Port().Properties, ) {
			return 
		}
	}
	 := .AddPort()
	.SetPortNode(, &)
	.SetPortSide(, )
	basegraph.Set(&.Port().Properties, , true)
	return 
}

func ( *LGraphArena) ( LGraphID) LayerID {
	 := .AddLayer()
	.Graph().Layers = append(.Graph().Layers, )
	return 
}

func ( *LGraphArena) ( LNodeID) {
	 := .Node()
	if .Layer != nil {
		.SetNodeLayer(, nil)
	}
	 := .Graph(.NodeGraph())
	.LayerlessNodes = removeNode(.LayerlessNodes, )
}

// CreateExternalPortDummy is the arena-backed counterpart of
// LGraphUtil.createExternalPortDummy. The caller deliberately decides when to
// add the returned node to Graph.LayerlessNodes, matching the Java helper.
func ( *LGraphArena) (
	 *basegraph.PropertyMap,
	 PortConstraints,
	 PortSide,
	 int,
	 *geometry.Point,
	 *geometry.Point,
	 geometry.Point,
	 Direction,
	 LGraphID,
) LNodeID {
	 := 
	 := .AddNode()
	 := .Node()
	.Type = ExternalPortNode
	basegraph.Set(&.Properties, ExternalPortSize, )
	basegraph.Set(&.Properties, PortConstraintsOption, PortConstraintsFixedPosition)
	 := basegraph.Get(, PortBorderOffset)
	basegraph.Set(&.Properties, PortBorderOffset, )

	 := .AddPort()
	.SetPortNode(, &)

	if !.IsSideFixed() {
		if  == DirectionUndefined {
			 = DirectionRight
		}
		 = DirectionPortSide()
		if  < 0 {
			 = .Opposed()
		}
		basegraph.Set(, PortSideOption, )
	}

	,  := basegraph.TryGet(, PortAnchor)
	if ! {
		 = geometry.Point{X: .X / 2, Y: .Y / 2}
	}
	 := .Port()
	switch  {
	case WestSide:
		basegraph.Set(&.Properties, LayerConstraintOption, LayerConstraintFirstSeparate)
		basegraph.Set(&.Properties, InternalEdgeConstraint, EdgeConstraintOutgoingOnly)
		.Size.Y = .Y
		if  < 0 {
			.Size.X = -
		}
		.SetPortSide(, EastSide)
		if ! {
			.X = .X
		}
		.X -= .X
	case EastSide:
		basegraph.Set(&.Properties, LayerConstraintOption, LayerConstraintLastSeparate)
		basegraph.Set(&.Properties, InternalEdgeConstraint, EdgeConstraintIncomingOnly)
		.Size.Y = .Y
		if  < 0 {
			.Size.X = -
		}
		.SetPortSide(, WestSide)
		if ! {
			.X = 0
		}
	case NorthSide:
		basegraph.Set(&.Properties, InternalInLayerConstraint, InLayerConstraintTop)
		.Size.X = .X
		if  < 0 {
			.Size.Y = -
		}
		.SetPortSide(, SouthSide)
		if ! {
			.Y = .Y
		}
		.Y -= .Y
	case SouthSide:
		basegraph.Set(&.Properties, InternalInLayerConstraint, InLayerConstraintBottom)
		.Size.X = .X
		if  < 0 {
			.Size.Y = -
		}
		.SetPortSide(, NorthSide)
		if ! {
			.Y = 0
		}
	default:
		// ELK asserts here. RIGHT is its effective fallback whenever an
		// undefined direction reaches this helper.
		 = EastSide
		basegraph.Set(&.Properties, LayerConstraintOption, LayerConstraintLastSeparate)
		basegraph.Set(&.Properties, InternalEdgeConstraint, EdgeConstraintIncomingOnly)
		.SetPortSide(, WestSide)
	}
	.Position = 
	basegraph.Set(&.Properties, PortAnchor, )

	if .IsOrderFixed() {
		 := 0.0
		if  == PortConstraintsFixedOrder {
			if ,  := basegraph.TryGet(, PortIndex);  {
				 = float64()
				if  == SouthSide ||  == WestSide {
					 = -
				}
				basegraph.Set(&.Properties, PortRatioOrPosition, )
				basegraph.Set(&.Properties, ExternalPortSide, )
				return 
			}
		}
		if  != nil {
			if  == WestSide ||  == EastSide {
				 = .Y
				if .IsRatioFixed() &&  != nil && .Y != 0 {
					 /= .Y
				}
			} else {
				 = .X
				if .IsRatioFixed() &&  != nil && .X != 0 {
					 /= .X
				}
			}
		}
		basegraph.Set(&.Properties, PortRatioOrPosition, )
	}
	basegraph.Set(&.Properties, ExternalPortSide, )
	return 
}

// ExternalPortPosition calculates an external port's top-left position from
// its laid-out dummy and updates the dummy's border coordinate, exactly as
// LGraphUtil.getExternalPortPosition does.
func ( *LGraphArena) (
	 LGraphID,
	 LNodeID,
	,  float64,
) geometry.Point {
	,  := .Graph(), .Node()
	 := .Position.Add(.Size.Scale(.5))
	 := basegraph.Get(&.Properties, PortBorderOffset)
	switch basegraph.Get(&.Properties, ExternalPortSide) {
	case NorthSide:
		.X += .Padding.Left + .Offset.X - /2
		.Y = - - 
		.Position.Y = -(.Padding.Top +  + .Offset.Y)
	case EastSide:
		.X = .Size.X + .Padding.Left + .Padding.Right + 
		.Y += .Padding.Top + .Offset.Y - /2
		.Position.X = .Size.X + .Padding.Right +  - .Offset.X
	case SouthSide:
		.X += .Padding.Left + .Offset.X - /2
		.Y = .Size.Y + .Padding.Top + .Padding.Bottom + 
		.Position.Y = .Size.Y + .Padding.Bottom +  - .Offset.Y
	case WestSide:
		.X = - - 
		.Y += .Padding.Top + .Offset.Y - /2
		.Position.X = -(.Padding.Left +  + .Offset.X)
	}
	return 
}

func ( *LGraphArena) (,  LNodeID) bool {
	 := 
	for {
		 := .Graph(.NodeGraph())
		if .ParentNode == nil {
			return false
		}
		 = *.ParentNode
		if  ==  {
			return true
		}
	}
}

// ResizeNode mirrors LGraphUtil.resizeNode. ELK intentionally performs its
// ratio and delta calculations as float32 even though coordinates are doubles.
func ( *LGraphArena) (
	 LNodeID,
	 geometry.Point,
	,  bool,
) {
	 := .Node()
	 := .Size
	 := float32(.X / .X)
	 := float32(.Y / .Y)
	 := float32(.X - .X)
	 := float32(.Y - .Y)
	if  {
		 := basegraph.Get(&.Properties, PortConstraintsOption) == PortConstraintsFixedPosition
		for ,  := range append([]LPortID(nil), .Ports...) {
			 := .Port()
			switch .Side {
			case NorthSide:
				if ! {
					.Position.X *= float64()
				}
			case EastSide:
				.Position.X += float64()
				if ! {
					.Position.Y *= float64()
				}
			case SouthSide:
				if ! {
					.Position.X *= float64()
				}
				.Position.Y += float64()
			case WestSide:
				if ! {
					.Position.Y *= float64()
				}
			}
		}
	}
	if  {
		for ,  := range append([]LLabelID(nil), .Labels...) {
			 := .Label()
			 := .Position.X + .Size.X/2
			 := .Position.Y + .Size.Y/2
			 :=  / .X
			 :=  / .Y
			if + >= 1 {
				if - > 0 &&  >= 0 {
					.Position.X += float64()
					.Position.Y += float64() * 
				} else if - < 0 &&  >= 0 {
					.Position.X += float64() * 
					.Position.Y += float64()
				}
			}
		}
	}
	.Size = 
	basegraph.Set(&.Properties, NodeSizeConstraints, []string{})
}