// 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 
	
)

// GraphTransformMode selects one of GraphTransformer's two passes.
type GraphTransformMode uint8

const (
	ToInternalLTR GraphTransformMode = iota
	ToInputDirection
)

// TransformGraph applies ELK Layered's direction normalization. The internal
// layered phases always operate left-to-right; the second pass restores the
// input direction after routing.
func ( *layered.LGraphArena,  layered.LGraphID,  GraphTransformMode) {
	 := .Graph()
	 := .GraphAllNodes()
	 := basegraph.Get(&.Properties, layered.DirectionOption)
	 := basegraph.Get(&.Properties, layered.DirectionCongruencyOption)

	if  == layered.DirectionCongruencyReading {
		switch  {
		case layered.DirectionLeft:
			mirrorAllX(, , )
		case layered.DirectionDown:
			transposeAll(, , )
		case layered.DirectionUp:
			if  == ToInternalLTR {
				transposeAll(, , )
				mirrorAllY(, , )
			} else {
				mirrorAllY(, , )
				transposeAll(, , )
			}
		}
		return
	}

	if  == ToInternalLTR {
		switch  {
		case layered.DirectionLeft:
			mirrorAllX(, , )
			mirrorAllY(, , )
		case layered.DirectionDown:
			transposeAll(, , )
			mirrorAllX(, , )
		case layered.DirectionUp:
			mirrorAllX(, , )
			transposeAll(, , )
		}
	} else {
		switch  {
		case layered.DirectionLeft:
			mirrorAllX(, , )
			mirrorAllY(, , )
		case layered.DirectionDown:
			mirrorAllX(, , )
			transposeAll(, , )
		case layered.DirectionUp:
			transposeAll(, , )
			mirrorAllX(, , )
		}
	}
}

func mirrorAllX( *layered.LGraphArena,  layered.LGraphID,  []layered.LNodeID) {
	 := .Graph()
	 := 0.0
	if .Size.X == 0 {
		for ,  := range  {
			 := .Node()
			 = math.Max(, .Position.X+.Size.X+.Margin.Right)
		}
	} else {
		 = .Size.X - .Offset.X
	}
	 -= .Offset.X

	for ,  := range  {
		 := .Node()
		.Position.X =  - .Size.X - .Position.X
		.Padding.Left, .Padding.Right = .Padding.Right, .Padding.Left
		if ,  := basegraph.TryGet(&.Properties, layered.PositionOption);  {
			.X =  - .Size.X - .X
			basegraph.Set(&.Properties, layered.PositionOption, )
		}
		switch basegraph.Get(&.Properties, layered.AlignmentOption) {
		case layered.AlignmentLeft:
			basegraph.Set(&.Properties, layered.AlignmentOption, layered.AlignmentRight)
		case layered.AlignmentRight:
			basegraph.Set(&.Properties, layered.AlignmentOption, layered.AlignmentLeft)
		}
		for ,  := range .Ports {
			 := .Port()
			.Position.X = .Size.X - .Size.X - .Position.X
			.Anchor.X = .Size.X - .Anchor.X
			switch .Side {
			case layered.EastSide:
				.SetPortSide(, layered.WestSide)
			case layered.WestSide:
				.SetPortSide(, layered.EastSide)
			}
			for ,  := range .OutgoingEdges {
				 := .Edge()
				for  := range .BendPoints {
					.BendPoints[].X =  - .BendPoints[].X
				}
				if ,  := basegraph.TryGet(&.Properties, layered.JunctionPoints);  {
					for  := range  {
						[].X =  - [].X
					}
					basegraph.Set(&.Properties, layered.JunctionPoints, )
				}
				for ,  := range .Labels {
					 := .Label()
					.Position.X =  - .Size.X - .Position.X
				}
			}
			for ,  := range .Labels {
				 := .Label()
				.Position.X = .Size.X - .Size.X - .Position.X
			}
		}
		if .Type == layered.ExternalPortNode {
			 := basegraph.Get(&.Properties, layered.ExternalPortSide)
			if  == layered.EastSide {
				 = layered.WestSide
			} else if  == layered.WestSide {
				 = layered.EastSide
			}
			basegraph.Set(&.Properties, layered.ExternalPortSide, )
			switch basegraph.Get(&.Properties, layered.LayerConstraintOption) {
			case layered.LayerConstraintFirst:
				basegraph.Set(&.Properties, layered.LayerConstraintOption, layered.LayerConstraintLast)
			case layered.LayerConstraintFirstSeparate:
				basegraph.Set(&.Properties, layered.LayerConstraintOption, layered.LayerConstraintLastSeparate)
			case layered.LayerConstraintLast:
				basegraph.Set(&.Properties, layered.LayerConstraintOption, layered.LayerConstraintFirst)
			case layered.LayerConstraintLastSeparate:
				basegraph.Set(&.Properties, layered.LayerConstraintOption, layered.LayerConstraintFirstSeparate)
			}
		}
		for ,  := range .Labels {
			 := .Label()
			.Position.X = .Size.X - .Size.X - .Position.X
		}
	}
	.Padding.Left, .Padding.Right = .Padding.Right, .Padding.Left
	 := basegraph.Get(&.Properties, layered.NodeLabelsPadding)
	.Left, .Right = .Right, .Left
	basegraph.Set(&.Properties, layered.NodeLabelsPadding, )
}

func mirrorAllY( *layered.LGraphArena,  layered.LGraphID,  []layered.LNodeID) {
	 := .Graph()
	 := 0.0
	if .Size.Y == 0 {
		for ,  := range  {
			 := .Node()
			 = math.Max(, .Position.Y+.Size.Y+.Margin.Bottom)
		}
	} else {
		 = .Size.Y - .Offset.Y
	}
	 -= .Offset.Y

	for ,  := range  {
		 := .Node()
		.Position.Y =  - .Size.Y - .Position.Y
		.Padding.Top, .Padding.Bottom = .Padding.Bottom, .Padding.Top
		if ,  := basegraph.TryGet(&.Properties, layered.PositionOption);  {
			.Y =  - .Size.Y - .Y
			basegraph.Set(&.Properties, layered.PositionOption, )
		}
		switch basegraph.Get(&.Properties, layered.AlignmentOption) {
		case layered.AlignmentTop:
			basegraph.Set(&.Properties, layered.AlignmentOption, layered.AlignmentBottom)
		case layered.AlignmentBottom:
			basegraph.Set(&.Properties, layered.AlignmentOption, layered.AlignmentTop)
		}
		for ,  := range .Ports {
			 := .Port()
			.Position.Y = .Size.Y - .Size.Y - .Position.Y
			.Anchor.Y = .Size.Y - .Anchor.Y
			switch .Side {
			case layered.NorthSide:
				.SetPortSide(, layered.SouthSide)
			case layered.SouthSide:
				.SetPortSide(, layered.NorthSide)
			}
			for ,  := range .OutgoingEdges {
				 := .Edge()
				for  := range .BendPoints {
					.BendPoints[].Y =  - .BendPoints[].Y
				}
				if ,  := basegraph.TryGet(&.Properties, layered.JunctionPoints);  {
					for  := range  {
						[].Y =  - [].Y
					}
					basegraph.Set(&.Properties, layered.JunctionPoints, )
				}
				for ,  := range .Labels {
					 := .Label()
					.Position.Y =  - .Size.Y - .Position.Y
				}
			}
			for ,  := range .Labels {
				 := .Label()
				.Position.Y = .Size.Y - .Size.Y - .Position.Y
			}
		}
		if .Type == layered.ExternalPortNode {
			 := basegraph.Get(&.Properties, layered.ExternalPortSide)
			if  == layered.NorthSide {
				 = layered.SouthSide
			} else if  == layered.SouthSide {
				 = layered.NorthSide
			}
			basegraph.Set(&.Properties, layered.ExternalPortSide, )
			switch basegraph.Get(&.Properties, layered.InternalInLayerConstraint) {
			case layered.InLayerConstraintTop:
				basegraph.Set(&.Properties, layered.InternalInLayerConstraint, layered.InLayerConstraintBottom)
			case layered.InLayerConstraintBottom:
				basegraph.Set(&.Properties, layered.InternalInLayerConstraint, layered.InLayerConstraintTop)
			}
		}
		for ,  := range .Labels {
			 := .Label()
			.Position.Y = .Size.Y - .Size.Y - .Position.Y
		}
	}
	.Padding.Top, .Padding.Bottom = .Padding.Bottom, .Padding.Top
	 := basegraph.Get(&.Properties, layered.NodeLabelsPadding)
	.Top, .Bottom = .Bottom, .Top
	basegraph.Set(&.Properties, layered.NodeLabelsPadding, )
}

func transposeAll( *layered.LGraphArena,  layered.LGraphID,  []layered.LNodeID) {
	 := .Graph()
	for ,  := range  {
		 := .Node()
		transposePoint(&.Position)
		transposePoint(&.Size)
		transposeSpacing(&.Padding)
		if ,  := basegraph.TryGet(&.Properties, layered.NodeSizeMinimum);  {
			transposePoint(&)
			basegraph.Set(&.Properties, layered.NodeSizeMinimum, )
		}
		switch basegraph.Get(&.Properties, layered.AlignmentOption) {
		case layered.AlignmentLeft:
			basegraph.Set(&.Properties, layered.AlignmentOption, layered.AlignmentTop)
		case layered.AlignmentRight:
			basegraph.Set(&.Properties, layered.AlignmentOption, layered.AlignmentBottom)
		case layered.AlignmentTop:
			basegraph.Set(&.Properties, layered.AlignmentOption, layered.AlignmentLeft)
		case layered.AlignmentBottom:
			basegraph.Set(&.Properties, layered.AlignmentOption, layered.AlignmentRight)
		}
		if ,  := basegraph.TryGet(&.Properties, layered.PositionOption);  {
			transposePoint(&)
			basegraph.Set(&.Properties, layered.PositionOption, )
		}
		for ,  := range .Ports {
			 := .Port()
			transposePoint(&.Position)
			transposePoint(&.Anchor)
			transposePoint(&.Size)
			.SetPortSide(, transposePortSide(.Side))
			for ,  := range .OutgoingEdges {
				 := .Edge()
				for  := range .BendPoints {
					transposePoint(&.BendPoints[])
				}
				if ,  := basegraph.TryGet(&.Properties, layered.JunctionPoints);  {
					for  := range  {
						transposePoint(&[])
					}
					basegraph.Set(&.Properties, layered.JunctionPoints, )
				}
				for ,  := range .Labels {
					 := .Label()
					transposePoint(&.Position)
					transposePoint(&.Size)
				}
			}
			for ,  := range .Labels {
				 := .Label()
				transposePoint(&.Position)
				transposePoint(&.Size)
			}
		}
		if .Type == layered.ExternalPortNode {
			basegraph.Set(&.Properties, layered.ExternalPortSide,
				transposePortSide(basegraph.Get(&.Properties, layered.ExternalPortSide)))
			transposeExternalConstraints()
		}
		for ,  := range .Labels {
			 := .Label()
			transposePoint(&.Position)
			transposePoint(&.Size)
		}
	}
	transposePoint(&.Offset)
	transposePoint(&.Size)
	transposeSpacing(&.Padding)
	 := basegraph.Get(&.Properties, layered.NodeLabelsPadding)
	transposeSpacing(&)
	basegraph.Set(&.Properties, layered.NodeLabelsPadding, )
	 := basegraph.Get(&.Properties, layered.EdgeLabelSideSelectionOption)
	switch  {
	case layered.EdgeLabelSideAlwaysUp:
		 = layered.EdgeLabelSideAlwaysDown
	case layered.EdgeLabelSideAlwaysDown:
		 = layered.EdgeLabelSideAlwaysUp
	case layered.EdgeLabelSideDirectionUp:
		 = layered.EdgeLabelSideDirectionDown
	case layered.EdgeLabelSideDirectionDown:
		 = layered.EdgeLabelSideDirectionUp
	case layered.EdgeLabelSideSmartUp:
		 = layered.EdgeLabelSideSmartDown
	case layered.EdgeLabelSideSmartDown:
		 = layered.EdgeLabelSideSmartUp
	}
	basegraph.Set(&.Properties, layered.EdgeLabelSideSelectionOption, )
}

func transposeExternalConstraints( *layered.LNode) {
	 := basegraph.Get(&.Properties, layered.LayerConstraintOption)
	 := basegraph.Get(&.Properties, layered.InternalInLayerConstraint)
	switch {
	case  == layered.LayerConstraintFirstSeparate:
		basegraph.Set(&.Properties, layered.LayerConstraintOption, layered.LayerConstraintNone)
		basegraph.Set(&.Properties, layered.InternalInLayerConstraint, layered.InLayerConstraintTop)
	case  == layered.LayerConstraintLastSeparate:
		basegraph.Set(&.Properties, layered.LayerConstraintOption, layered.LayerConstraintNone)
		basegraph.Set(&.Properties, layered.InternalInLayerConstraint, layered.InLayerConstraintBottom)
	case  == layered.InLayerConstraintTop:
		basegraph.Set(&.Properties, layered.LayerConstraintOption, layered.LayerConstraintFirstSeparate)
		basegraph.Set(&.Properties, layered.InternalInLayerConstraint, layered.InLayerConstraintNone)
	case  == layered.InLayerConstraintBottom:
		basegraph.Set(&.Properties, layered.LayerConstraintOption, layered.LayerConstraintLastSeparate)
		basegraph.Set(&.Properties, layered.InternalInLayerConstraint, layered.InLayerConstraintNone)
	}
}

func transposePoint( *geometry.Point) { .X, .Y = .Y, .X }

func transposeSpacing( *geometry.Spacing) {
	.Top, .Right, .Bottom, .Left =
		.Left, .Bottom, .Right, .Top
}

func transposePortSide( layered.PortSide) layered.PortSide {
	switch  {
	case layered.NorthSide:
		return layered.WestSide
	case layered.WestSide:
		return layered.NorthSide
	case layered.SouthSide:
		return layered.EastSide
	case layered.EastSide:
		return layered.SouthSide
	default:
		return layered.UndefinedSide
	}
}