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

import (
	

	
	basegraph 
	
)

// RoutingDirection is distinct from a graph direction because routing around
// northern and southern external ports changes direction within one graph.
type RoutingDirection uint8

const (
	WestToEast RoutingDirection = iota
	NorthToSouth
	SouthToNorth
)

type routingDirectionStrategy struct {
	direction             RoutingDirection
	createdJunctionPoints map[geometry.Point]struct{}
}

func newRoutingDirectionStrategy( RoutingDirection) *routingDirectionStrategy {
	return &routingDirectionStrategy{
		direction:             ,
		createdJunctionPoints: make(map[geometry.Point]struct{}),
	}
}

func ( *routingDirectionStrategy) () layered.PortSide {
	switch .direction {
	case WestToEast:
		return layered.EastSide
	case NorthToSouth:
		return layered.SouthSide
	case SouthToNorth:
		return layered.NorthSide
	default:
		panic("orthogonal: invalid routing direction")
	}
}

func ( *routingDirectionStrategy) () layered.PortSide {
	switch .direction {
	case WestToEast:
		return layered.WestSide
	case NorthToSouth:
		return layered.NorthSide
	case SouthToNorth:
		return layered.SouthSide
	default:
		panic("orthogonal: invalid routing direction")
	}
}

func ( *routingDirectionStrategy) ( *layered.LGraphArena,  layered.LPortID) float64 {
	 := .Port()
	 := .Node(*.Node)
	if .direction == WestToEast {
		return .Position.Y + .Position.Y + .Anchor.Y
	}
	return .Position.X + .Position.X + .Anchor.X
}

func absoluteAnchor( *layered.LGraphArena,  layered.LPortID) geometry.Point {
	 := .Port()
	return .Node(*.Node).Position.Add(.Position).Add(.Anchor)
}

func ( *routingDirectionStrategy) (
	 *layered.LGraphArena,
	 *HyperEdgeSegment,
	,  float64,
) {
	if .IsDummy() {
		return
	}
	switch .direction {
	case WestToEast:
		.calculateHorizontalBends(, , , )
	case NorthToSouth:
		.calculateVerticalBends(, , , , 1)
	case SouthToNorth:
		.calculateVerticalBends(, , , , -1)
	default:
		panic("orthogonal: invalid routing direction")
	}
}

func ( *routingDirectionStrategy) (
	 *layered.LGraphArena,
	 *HyperEdgeSegment,
	,  float64,
) {
	 :=  + float64(.RoutingSlot)*
	for ,  := range .Ports {
		 := absoluteAnchor(, ).Y
		for ,  := range .Port().OutgoingEdges {
			if .EdgeIsSelfLoop() {
				continue
			}
			 := absoluteAnchor(, *.Edge().Target).Y
			if math.Abs(-) <= Tolerance {
				continue
			}
			,  := , 
			 := geometry.Point{X: , Y: }
			.Edge().BendPoints = append(.Edge().BendPoints, )
			.addJunctionPointIfNecessary(, , , , true)
			if  := .SplitPartner;  != nil {
				 := .IncomingCoordinates[0]
				 = geometry.Point{X: , Y: }
				.Edge().BendPoints = append(.Edge().BendPoints, )
				.addJunctionPointIfNecessary(, , , , true)
				 =  + float64(.RoutingSlot)*
				 = 
				 = geometry.Point{X: , Y: }
				.Edge().BendPoints = append(.Edge().BendPoints, )
				.addJunctionPointIfNecessary(, , , , true)
			}
			 = geometry.Point{X: , Y: }
			.Edge().BendPoints = append(.Edge().BendPoints, )
			.addJunctionPointIfNecessary(, , , , true)
		}
	}
}

func ( *routingDirectionStrategy) (
	 *layered.LGraphArena,
	 *HyperEdgeSegment,
	, ,  float64,
) {
	 :=  + *float64(.RoutingSlot)*
	for ,  := range .Ports {
		 := absoluteAnchor(, ).X
		for ,  := range .Port().OutgoingEdges {
			if .EdgeIsSelfLoop() {
				continue
			}
			 := absoluteAnchor(, *.Edge().Target).X
			if math.Abs(-) <= Tolerance {
				continue
			}
			,  := , 
			 := geometry.Point{X: , Y: }
			.Edge().BendPoints = append(.Edge().BendPoints, )
			.addJunctionPointIfNecessary(, , , , false)
			if  := .SplitPartner;  != nil {
				 := .IncomingCoordinates[0]
				 = geometry.Point{X: , Y: }
				.Edge().BendPoints = append(.Edge().BendPoints, )
				.addJunctionPointIfNecessary(, , , , false)
				 =  + *float64(.RoutingSlot)*
				 = 
				 = geometry.Point{X: , Y: }
				.Edge().BendPoints = append(.Edge().BendPoints, )
				.addJunctionPointIfNecessary(, , , , false)
			}
			 = geometry.Point{X: , Y: }
			.Edge().BendPoints = append(.Edge().BendPoints, )
			.addJunctionPointIfNecessary(, , , , false)
		}
	}
}

func ( *routingDirectionStrategy) (
	 *layered.LGraphArena,
	 layered.LEdgeID,
	 *HyperEdgeSegment,
	 geometry.Point,
	 bool,
) {
	 := .X
	if  {
		 = .Y
	}
	if ,  := .createdJunctionPoints[];  {
		return
	}
	 :=  > .Start &&  < .End
	 := false
	if len(.IncomingCoordinates) > 0 && len(.OutgoingCoordinates) > 0 {
		 = math.Abs(-.IncomingCoordinates[0]) < Tolerance &&
			math.Abs(-.OutgoingCoordinates[0]) < Tolerance
		 := .IncomingCoordinates[len(.IncomingCoordinates)-1]
		 := .OutgoingCoordinates[len(.OutgoingCoordinates)-1]
		 =  || math.Abs(-) < Tolerance &&
			math.Abs(-) < Tolerance
	}
	if  ||  {
		 := basegraph.Get(&.Edge().Properties, layered.JunctionPoints)
		 = append(, )
		basegraph.Set(&.Edge().Properties, layered.JunctionPoints, )
		.createdJunctionPoints[] = struct{}{}
	}
}

func ( *routingDirectionStrategy) () {
	clear(.createdJunctionPoints)
}