// Copyright (c) 2017 Kiel University and others.
// Copyright (c) 2017, 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
// http://www.eclipse.org/legal/epl-2.0.
//
// SPDX-License-Identifier: EPL-2.0

package radial

import (
	
	
	

	
	
	
	
	
	
)

type Provider struct{}

func ( *core.AlgorithmRegistry) error {
	return .Register(core.AlgorithmData{
		ID:     AlgorithmID,
		Create: func() core.LayoutProvider { return &Provider{} },
		KnownOptions: []string{
			Position.ID,
			SpacingNodeNode.ID,
			"org.eclipse.elk.nodeSize.constraints",
			basicoptions.NodeSizeFixedGraphSize.ID,
			"org.eclipse.elk.nodeSize.minimum",
			"org.eclipse.elk.nodeSize.options",
			"org.eclipse.elk.nodeLabels.placement",
			OmitNodeMicroLayout.ID,
			"org.eclipse.elk.portLabels.placement",
			CompactionStepSize.ID,
			Compactor.ID,
			Rotate.ID,
			RotationTargetAngle.ID,
			RotationComputeAdditionalWedgeSpace.ID,
			RotationOutgoingEdgeAngles.ID,
			OptimizationCriteria.ID,
			OrderID.ID,
			Radius.ID,
			Sorter.ID,
			WedgeCriteria.ID,
			CenterOnRoot.ID,
		},
	})
}

func (*Provider) ( *graph.Model,  graph.NodeID,  core.ProgressMonitor) error {
	 := .Node()
	if !graph.Get(&.Properties, OmitNodeMicroLayout) {
		nodespacing.CalculateLabelAndNodeSizes(, , nil)
		nodespacing.NewNodeMarginCalculator().Process(, )
	}
	 := findRoot(, )
	if  == nil {
		return fmt.Errorf("the given graph is not a tree")
	}
	 := graph.Get(&.Properties, Radius)
	if  == 0 {
		 = largestNode(, )
	}
	graph.Set(&.Properties, Radius, )
	.Begin("Radial layout", 5)
	positionGraph(, *, , graph.Get(&.Properties, Sorter), graph.Get(&.Properties, WedgeCriteria), graph.Get(&.Properties, OptimizationCriteria))
	.Worked(1)
	removeOverlaps(, *, graph.Get(&.Properties, SpacingNodeNode), graph.Get(&.Properties, Sorter))
	.Worked(1)
	switch graph.Get(&.Properties, Compactor) {
	case "RADIAL_COMPACTION":
		radialCompact(, *, graph.Get(&.Properties, SpacingNodeNode), graph.Get(&.Properties, CompactionStepSize), graph.Get(&.Properties, Sorter))
	case "WEDGE_COMPACTION":
		wedgeCompact(, *, graph.Get(&.Properties, SpacingNodeNode), graph.Get(&.Properties, CompactionStepSize), graph.Get(&.Properties, Sorter))
	}
	.Worked(1)
	if graph.Get(&.Properties, Rotate) {
		rotateGraph(, , *)
	}
	calculateGraphSize(, , *)
	.Worked(1)
	routeEdges(, *)
	if graph.Get(&.Properties, RotationOutgoingEdgeAngles) {
		calculateOutgoingEdgeAngles(, *)
	}
	.Worked(1)
	.Done()
	return nil
}

func findRoot( *graph.Model,  graph.NodeID) *graph.NodeID {
	for ,  := range .Node().Children {
		if len(allIncoming(, )) == 0 {
			 := 
			return &
		}
	}
	return nil
}

func treeParent( *graph.Model,  graph.NodeID) *graph.NodeID {
	 := allIncoming(, )
	if len() == 0 || len(.Edge([0]).Sources) == 0 {
		return nil
	}
	 := .ShapeNode(.Edge([0]).Sources[0])
	return &
}

func allIncoming( *graph.Model,  graph.NodeID) []graph.EdgeID {
	 := append([]graph.EdgeID(nil), .Node().IncomingEdges...)
	for ,  := range .Node().Ports {
		 = append(, .Port().IncomingEdges...)
	}
	return 
}

// successors retains RadialUtil.getSuccessors's unusual port behavior: an
// edge whose first source is a port is ignored entirely.
func successors( *graph.Model,  graph.NodeID) []graph.NodeID {
	 := make(map[graph.NodeID]struct{}, len(.Node().Children))
	for ,  := range .Node().Children {
		[] = struct{}{}
	}
	 := make([]graph.NodeID, 0)
	for ,  := range .Node().OutgoingEdges {
		 := .Edge()
		if len(.Sources) == 0 || .Sources[0].Kind == graph.PortShape || len(.Targets) == 0 {
			continue
		}
		 := .ShapeNode(.Targets[0])
		if ,  := []; ! {
			 = append(, )
		}
	}
	return 
}

func largestNode( *graph.Model,  graph.NodeID) float64 {
	 := 0.0
	for ,  := range .Node().Children {
		 := .Node()
		 = math.Max(, radialLength(.Shape.Width, .Shape.Height))
		 = math.Max(, (, ))
	}
	return 
}

func leafCount( *graph.Model,  graph.NodeID) int {
	 := successors(, )
	if len() == 0 {
		return 1
	}
	 := 0
	for ,  := range  {
		 += (, )
	}
	return 
}

func wedgeSpace( *graph.Model,  graph.NodeID,  string) float64 {
	if  == "LEAF_NUMBER" {
		return float64(leafCount(, ))
	}
	 := .Node().Shape
	 := math.Sqrt(.Width*.Width + .Height*.Height)
	 := 0.0
	for ,  := range successors(, ) {
		 += (, , )
	}
	return math.Max(, )
}

func positionGraph( *graph.Model,  graph.NodeID,  float64, , ,  string) {
	 := 0.0
	if  != "NONE" {
		 := math.MaxFloat64
		for  := 0;  < 360; ++ {
			 := float64() * math.Pi / 180
			positionNodes(, , , , , , 0, 0, 2*math.Pi, )
			 := evaluate(, , )
			if  <  {
				,  = , 
			}
		}
	}
	positionNodes(, , , , , , 0, 0, 2*math.Pi, )
}

func positionNodes( *graph.Model, ,  graph.NodeID,  float64, ,  string, , , ,  float64) {
	 := (+)/2 + 
	 :=  * javacompat.JSMathCos()
	 :=  * javacompat.JSMathSin()
	 := &.Node().Shape
	.SetLocation(-.Width/2, -.Height/2)
	 := wedgeSpace(, , )
	// EadesRadial 0.8.1 computes tau with
	// currentRadius/currentRadius + radius. For every useful radius this is
	// NaN or outside acos's domain, so the observable branch uses the whole
	// available wedge.
	 := ( - ) / 
	 := 
	 := successors(, )
	sortNodes(, , , )
	for ,  := range  {
		 := wedgeSpace(, , )
		(, , , , , , +, , +*, )
		 +=  * 
	}
}

func sortNodes( *graph.Model,  graph.NodeID,  []graph.NodeID,  string) {
	switch  {
	case "ID":
		sort.SliceStable(, func(,  int) bool {
			return graph.Get(&.Node([]).Properties, OrderID) < graph.Get(&.Node([]).Properties, OrderID)
		})
	case "POLAR_COORDINATE":
		initializePolarSorter(, )
		sort.SliceStable(, func(,  int) bool {
			return graph.Get(&.Node([]).Properties, OrderID) < graph.Get(&.Node([]).Properties, OrderID)
		})
	}
}

func initializePolarSorter( *graph.Model,  graph.NodeID) {
	 := successors(, )
	polarSort(, , 0)
	setPolarIDs(, , 0)
}

func setPolarIDs( *graph.Model,  []graph.NodeID,  int) int {
	,  := , 0
	for ,  := range  {
		graph.Set(&.Node().Properties, OrderID, )
		++
		 := successors(, )
		 := .Node().Shape
		 := math.Atan2(.Y+.Height/2, .X+.Width/2)
		if  < 0 {
			 += 2 * math.Pi
		}
		switch {
		case  < math.Pi/4 ||  > 7*math.Pi/4:
			polarSort(, , math.Pi)
		case  <= 7*math.Pi/4 &&  > 5*math.Pi/4:
			polarSort(, , 3*math.Pi/2)
		case  <= 5*math.Pi/4 &&  > 3*math.Pi/4:
			polarSort(, , 0)
		case  <= 3*math.Pi/4:
			polarSort(, , math.Pi/2)
		}
		 = (, , )
	}
	return 
}

func polarSort( *graph.Model,  []graph.NodeID,  float64) {
	sort.SliceStable(, func(,  int) bool {
		,  := polarArc(graph.Get(&.Node([]).Properties, Position), ), polarArc(graph.Get(&.Node([]).Properties, Position), )
		if math.Abs(-) <= 1e-10 {
			return false
		}
		return  < 
	})
}

func polarArc( geometry.Point,  float64) float64 {
	 := math.Atan2(.Y, .X)
	if  < 0 {
		 += 2 * math.Pi
	}
	 += 
	if  > 2*math.Pi {
		 -= 2 * math.Pi
	}
	return 
}

func evaluate( *graph.Model,  graph.NodeID,  string) float64 {
	switch  {
	case "EDGE_LENGTH":
		return evaluateEdgeLength(, )
	case "EDGE_LENGTH_BY_POSITION":
		return evaluateEdgeLengthByPosition(, )
	case "CROSSING_MINIMIZATION_BY_POSITION":
		return evaluateCrossings(, )
	default:
		return 0
	}
}

func evaluateEdgeLength( *graph.Model,  graph.NodeID) float64 {
	 := 0.0
	 := .Node().Shape
	for ,  := range successors(, ) {
		 := .Node().Shape
		 := geometry.Point{X: .X + .Width/2, Y: .Y + .Height/2}
		 := geometry.Point{X: .X + .Width/2, Y: .Y + .Height/2}
		,  = clippedEndpoints(, , geometry.Point{X: .Width, Y: .Height}, geometry.Point{X: .Width, Y: .Height})
		 := .X - .X
		 := .Y - .Y
		// EdgeLengthOptimization uses Math.sqrt(x*x + y*y), rather than
		// Math.hypot. The rounding difference is observable when rotationally
		// equivalent offsets tie and ELK retains the first minimum.
		 += math.Sqrt(* + *)
	}
	return 
}

func evaluateEdgeLengthByPosition( *graph.Model,  graph.NodeID) float64 {
	 := 0.0
	 := .Node().Shape
	for ,  := range successors(, ) {
		 := .Node().Shape
		 := geometry.Point{X: .X + .Width/2, Y: .Y + .Height/2}
		 := graph.Get(&.Node().Properties, Position)
		 := geometry.Point{X: .X + .X + .Width/2, Y: .Y + .Y + .Height}
		 += radialLength(.X-.X, .Y-.Y)
	}
	return 
}

func evaluateCrossings( *graph.Model,  graph.NodeID) float64 {
	 := successors(, )
	 := 0
	for  := range  {
		for  :=  + 1;  < len(); ++ {
			if crossing(, , [], []) {
				++
			}
		}
	}
	return float64()
}

func crossing( *graph.Model, , ,  graph.NodeID) bool {
	 := .Node().Shape
	// CrossingMinimizationPosition intentionally uses root.x/width for both
	// axes in ELK 0.8.1.
	 := .X + .Width/2
	 := .X + .Width/2
	 := func( graph.NodeID) (geometry.Point, geometry.Point, float64, float64) {
		 := .Node().Shape
		 := geometry.Point{X: .X + .Width/2, Y: .Y + .Height/2}
		 := graph.Get(&.Node().Properties, Position)
		.X += 
		.Y += 
		graph.Set(&.Node().Properties, Position, )
		 := (.Y - .Y) / (.X - .X)
		 := .Y - *.X
		return , , , 
	}
	, , ,  := ()
	, , ,  := ()
	 := ( - ) / ( - )
	if .X <  && .X <  ||  < .X &&  < .X {
		return false
	}
	if .X <  && .X <  ||  < .X &&  < .X {
		return false
	}
	return true
}

type radialMover struct {
	model   *graph.Model
	root    graph.NodeID
	spacing float64
	step    int
}

func ( radialMover) ( []graph.NodeID,  bool) {
	 := .model.Node(.root).Shape
	 := geometry.Point{X: .X + .Width/2, Y: .Y + .Height/2}
	for ,  := range  {
		 := &.model.Node().Shape
		 := geometry.Point{X: .X + .Width/2, Y: .Y + .Height/2}
		 := .Sub()
		 := radialLength(.X, .Y)
		 = .Scale(float64(.step) / )
		if  {
			 = .Sub()
		} else {
			 = .Add()
		}
		.SetLocation(.X-.Width/2, .Y-.Height/2)
	}
}

func ( radialMover) ( graph.NodeID,  float64) {
	 := .model.Node(.root).Shape
	 := &.model.Node().Shape
	 := geometry.Point{X: .X + .Width/2, Y: .Y + .Height/2}
	 := geometry.Point{X: .X + .Width/2, Y: .Y + .Height/2}
	 := .Sub()
	 := radialLength(.X, .Y)
	.SetLocation(.X+.X/*, .Y+.Y/*)
}

func ( radialMover) (,  graph.NodeID) bool {
	,  := .model.Node().Shape, .model.Node().Shape
	,  := .X-.spacing/2, .X-.spacing/2
	,  := .Y-.spacing/2, .Y-.spacing/2
	,  := .Width+.spacing, .Width+.spacing
	,  := .Height+.spacing, .Height+.spacing
	return  < + &&  <  &&  < + &&  <  ||
		 < + &&  <  &&  < + &&  <  ||
		 < + &&  <  &&  <  &&  < + ||
		 < + &&  <  &&  < + &&  < 
}

func ( radialMover) ( []graph.NodeID) bool {
	if len() < 2 {
		return false
	}
	for  := range  {
		if .overlap([], [(+1)%len()]) {
			return true
		}
	}
	return false
}

func removeOverlaps( *graph.Model,  graph.NodeID,  float64,  string) {
	 := radialMover{model: , root: , spacing: , step: 1}
	var  func([]graph.NodeID)
	 = func( []graph.NodeID) {
		if len() == 0 {
			return
		}
		 := make([]geometry.Point, len())
		for ,  := range  {
			[] = .Node().Shape.Position()
		}
		for .overlapLayer() {
			.contractLayer(, false)
		}
		 := .Node([0]).Shape.Position()
		 := radialLength(.X-[0].X, .Y-[0].Y)
		 := uniqueNextLevel(, )
		if len() > 0 {
			for ,  := range  {
				.moveNode(, )
			}
		}
		sortNodes(, , , )
		()
	}
	(successors(, ))
}

func uniqueNextLevel( *graph.Model,  []graph.NodeID) []graph.NodeID {
	 := make(map[graph.NodeID]struct{})
	 := make([]graph.NodeID, 0)
	for ,  := range  {
		for ,  := range successors(, ) {
			if ,  := [];  {
				continue
			}
			[] = struct{}{}
			 = append(, )
		}
	}
	return 
}

func radialCompact( *graph.Model,  graph.NodeID,  float64,  int,  string) {
	 := radialMover{model: , root: , spacing: , step: }
	 := 0.0
	var  func(graph.NodeID) float64
	 = func( graph.NodeID) float64 {
		,  := .Node().Shape, .Node().Shape
		return radialLength(.X-.X, .Y-.Y)
	}
	var  func([]graph.NodeID)
	 = func( []graph.NodeID) {
		if len() == 0 {
			return
		}
		sortNodes(, , , )
		 := func() bool {
			if .overlapLayer() {
				return true
			}
			for ,  := range  {
				 := treeParent(, )
				if  != nil && .overlap(, *) {
					return true
				}
				if ()- <=  {
					return true
				}
			}
			return false
		}
		 := false
		for !() {
			.contractLayer(, true)
			 = true
		}
		if  {
			.contractLayer(, false)
		}
		 := make([]graph.NodeID, 0)
		for ,  := range  {
			 = append(, successors(, )...)
		}
		 = ([0])
		()
	}
	(successors(, ))
}

func wedgeCompact( *graph.Model,  graph.NodeID,  float64,  int,  string) {
	 := radialMover{model: , root: , spacing: , step: }
	 := successors(, )
	sortNodes(, , , )
	 := make(map[graph.NodeID][]graph.NodeID)
	 := make(map[graph.NodeID][]graph.NodeID)
	for ,  := range  {
		[] = append([], )
		[] = append([], )
		 := successors(, )
		for len() > 0 {
			sortNodes(, , , )
			[] = append([], [0])
			[] = append([], [len()-1])
			 := make([]graph.NodeID, 0)
			for ,  := range  {
				 = append(, successors(, )...)
			}
			 = 
		}
	}
	 := func(,  graph.NodeID,  bool) bool {
		 := []
		if  {
			 = []
		}
		for ,  := range  {
			if .overlap(, ) {
				return true
			}
		}
		return false
	}
	var  func([]graph.NodeID, graph.NodeID, graph.NodeID)
	 = func( []graph.NodeID, ,  graph.NodeID) {
		if len() == 0 {
			return
		}
		sortNodes(, , , )
		 := make([]graph.NodeID, 0)
		for ,  := range  {
			if  := treeParent(, );  != nil && !containsNode(, *) {
				 = append(, *)
			}
		}
		 := func() bool {
			if (, [0], false) || (, [len()-1], true) || .overlapLayer() {
				return true
			}
			for ,  := range  {
				for ,  := range  {
					if .overlap(, ) {
						return true
					}
				}
			}
			return false
		}
		 := false
		for !() {
			.contractLayer(, true)
			 = true
		}
		if  {
			.contractLayer(, false)
		}
		 := make([]graph.NodeID, 0)
		for ,  := range  {
			 = append(, successors(, )...)
		}
		(, , )
	}
	for  := 0;  < 2; ++ {
		for ,  := range  {
			 := [(+len()-1)%len()]
			 := [(+1)%len()]
			([]graph.NodeID{}, , )
		}
	}
}

func containsNode( []graph.NodeID,  graph.NodeID) bool {
	for ,  := range  {
		if  ==  {
			return true
		}
	}
	return false
}

func rotateGraph( *graph.Model, ,  graph.NodeID) {
	 := .Node()
	 := graph.Get(&.Properties, RotationTargetAngle)
	if graph.Get(&.Properties, RotationComputeAdditionalWedgeSpace) {
		 := .Node().OutgoingEdges
		if len() > 0 {
			,  := .Edge([0]), .Edge([len()-1])
			if len(.Targets) > 0 && len(.Targets) > 0 {
				 := .Node(.ShapeNode(.Targets[0]))
				 := .Node(.ShapeNode(.Targets[0]))
				 := geometry.Point{X: .Shape.X + .Shape.Width/2, Y: .Shape.Y + .Shape.Height/2}
				 := geometry.Point{X: .Shape.X + .Shape.Width/2, Y: .Shape.Y + .Shape.Height/2}
				 := 
				if  <= 0 {
					 += 2 * math.Pi
				}
				 := math.Acos((.X*.X + .Y*.Y) /
					(math.Hypot(.X, .Y) * math.Hypot(.X, .Y)))
				if  <= 0 {
					 += 2 * math.Pi
				}
				 := math.Atan2(.Y, .X)
				if  <= 0 {
					 += 2 * math.Pi
				}
				 = math.Pi - ( -  + /2)
			}
		}
	}
	,  := javacompat.JSMathSin(), javacompat.JSMathCos()
	for ,  := range .Children {
		 := .Node()
		 := .Shape.X + .Shape.Width/2
		 := .Shape.Y + .Shape.Height/2
		 := float64( * )
		 := float64( * )
		 := float64( * )
		 := float64( * )
		 := float64( - )
		 := float64( + )
		.Shape.SetLocation(-.Shape.Width/2, -.Shape.Height/2)
	}
}

func radialLength(,  float64) float64 {
	// KVector.length() evaluates each square and their sum as separate
	// JavaScript binary64 operations. Keep Go from fusing either product.
	 := float64( * )
	 := float64( * )
	return math.Sqrt(float64( + ))
}

func calculateGraphSize( *graph.Model, ,  graph.NodeID) {
	,  := math.MaxFloat64, math.MaxFloat64
	,  := math.SmallestNonzeroFloat64, math.SmallestNonzeroFloat64
	for ,  := range .Node().Children {
		 := .Node()
		 := graph.Get(&.Properties, nodespacing.Margins)
		 = math.Min(, .Shape.X-.Left)
		 = math.Min(, .Shape.Y-.Top)
		 = math.Max(, .Shape.X+.Shape.Width+.Right)
		 = math.Max(, .Shape.Y+.Shape.Height+.Bottom)
	}
	 := graph.Get(&.Node().Properties, Padding)
	 := geometry.Point{X:  - .Left, Y:  - .Top}
	 :=  -  + .Left + .Right
	 :=  -  + .Top + .Bottom
	 := .Node()
	if graph.Get(&.Properties, CenterOnRoot) {
		 := .Node()
		 := graph.Get(&.Properties, nodespacing.Margins)
		 := .Shape.X + .Shape.Width/2 + (.Left+.Right)/2 - .X
		 := .Shape.Y + .Shape.Height/2 + (.Top+.Bottom)/2 - .Y
		,  := -, -
		if  < /2 {
			 :=  - 
			 += 
			.X -= 
		} else {
			 +=  - 
		}
		if  < /2 {
			 :=  - 
			 += 
			.Y -= 
		} else {
			 +=  - 
		}
	}
	for ,  := range .Node().Children {
		 := .Node()
		.Shape.SetLocation(.Shape.X-.X, .Shape.Y-.Y)
	}
	if !graph.Get(&.Properties, basicoptions.NodeSizeFixedGraphSize) {
		.Shape.SetDimensions(, )
	}
	graph.Set(&.Properties, core.ChildAreaWidth, -.Left-.Right)
	graph.Set(&.Properties, core.ChildAreaHeight, -.Top-.Bottom)
}

func calculateOutgoingEdgeAngles( *graph.Model,  graph.NodeID) {
	for ,  := range .Node().OutgoingEdges {
		 := .Edge()
		if len(.Sections) == 0 || len(.Targets) == 0 {
			continue
		}
		 := .Section(.Sections[0])
		 := math.Atan2(.End.Y-.Start.Y, .End.X-.Start.X)
		 := .ShapeNode(.Targets[0])
		graph.Set(&.Node().Properties, RotationTargetAngle, )
	}
}

func routeEdges( *graph.Model,  graph.NodeID) {
	for ,  := range .Node().OutgoingEdges {
		 := .Edge()
		if len(.Sources) == 0 || .Sources[0].Kind == graph.PortShape || len(.Targets) == 0 {
			continue
		}
		 := .ShapeNode(.Targets[0])
		if .ContainingNode == nil || .Node().Parent == nil || *.ContainingNode != *.Node().Parent {
			continue
		}
		,  := .Node().Shape, .Node().Shape
		 := geometry.Point{X: .X + .Width/2, Y: .Y + .Height/2}
		 := geometry.Point{X: .X + .Width/2, Y: .Y + .Height/2}
		,  = clippedEndpoints(, , geometry.Point{X: .Width, Y: .Height}, geometry.Point{X: .Width, Y: .Height})
		var  graph.SectionID
		if len(.Sections) == 0 {
			 = .AddSection()
		} else {
			 = .Sections[0]
		}
		 := .Section()
		.Start, .End = , 
		.StartPresent, .EndPresent = true, true
		.BendPoints = nil
		.BendPointsPresent = false
		(, )
	}
}

func clippedEndpoints(,  geometry.Point, ,  geometry.Point) (geometry.Point, geometry.Point) {
	 := .Sub()
	 := clipVector(, )
	 = .Sub()
	 = .Sub()
	 := clipVector(, )
	 = .Sub()
	 = .Add()
	return , 
}

func clipVector(,  geometry.Point) geometry.Point {
	,  := 1.0, 1.0
	if math.Abs(.X) > .X/2 {
		 = .X / 2 / math.Abs(.X)
	}
	if math.Abs(.Y) > .Y/2 {
		 = .Y / 2 / math.Abs(.Y)
	}
	return .Scale(math.Min(, ))
}