// Copyright (c) 2017 Kiel University and others.
// Copyright (c) 2026 The elk-go Authors.
//
// 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 spore

import (
	
	
	

	
	
	
	
	
)

type importer struct {
	model   *graph.Model
	parent  graph.NodeID
	graph   *sporeGraph
	spacing float64
}

func newImporter( *graph.Model,  graph.NodeID) *importer {
	return &importer{model: , parent: }
}

func ( *importer) () (*sporeGraph, error) {
	 := .model.Node(.parent)
	nodespacing.NewNodeMarginCalculator().Process(.model, .parent)

	.spacing = graph.Get(&.Properties, SpacingNodeNode)
	 := &sporeGraph{
		treeConstruction: graph.Get(&.Properties, TreeConstruction),
		compaction:       graph.Get(&.Properties, Compaction),
		orthogonal:       graph.Get(&.Properties, Orthogonal),
	}
	.graph = 

	for ,  := range .Children {
		 := .model.Node()
		,  := .Shape.Width/2, .Shape.Height/2
		 := geometry.Point{X: .Shape.X + , Y: .Shape.Y + }
		for .containsPoint() {
			.X += (rand.Float64() - 0.5) * 0.001
			.Y += (rand.Float64() - 0.5) * 0.001
		}
		 := &vertex{point: }
		 := graph.Get(&.Properties, nodespacing.Margins)
		 := geometry.Rectangle{
			X:      .X -  - .spacing/2 - .Left,
			Y:      .Y -  - .spacing/2 - .Top,
			Width:  .Shape.Width + .spacing + .Horizontal(),
			Height: .Shape.Height + .spacing + .Vertical(),
		}
		.vertices = append(.vertices, newNode(, , ))
	}
	if len(.vertices) == 0 {
		return , nil
	}

	switch graph.Get(&.Properties, RootSelection) {
	case RootFixed:
		,  := graph.TryGet(&.Properties, PreferredRoot)
		if ! {
			.preferredRoot = .vertices[0]
		} else {
			for ,  := range .vertices {
				if .model.Node(.origin).IdentifierPresent && .model.Node(.origin).Identifier ==  {
					.preferredRoot = 
				}
			}
		}
	case RootCenter:
		 := geometry.Point{
			X: .Shape.X + .Shape.Width/2,
			Y: .Shape.Y + .Shape.Height/2,
		}
		 := math.Inf(1)
		for ,  := range .vertices {
			 := .original.point.Distance()
			if  <  {
				 = 
				.preferredRoot = 
			}
		}
	default:
		return nil, fmt.Errorf("no SPOrE root selection implementation for %q", graph.Get(&.Properties, RootSelection))
	}

	switch graph.Get(&.Properties, SpanningTreeCost) {
	case CostCenterDistance:
		.cost = func( *tEdge) float64 { return .u.point.Distance(.v.point) }
	case CostCircleUnderlap:
		.cost = func( *tEdge) float64 {
			,  := .nodeFor(.u), .nodeFor(.v)
			return .u.point.Distance(.v.point) -
				.u.point.Distance(.rect.Position()) -
				.v.point.Distance(.rect.Position())
		}
	case CostRectangleUnderlap:
		.cost = func( *tEdge) float64 { return .nodeFor(.u).underlap(.nodeFor(.v)) }
	case CostInvertedOverlap:
		.cost = func( *tEdge) float64 {
			,  := .nodeFor(.u), .nodeFor(.v)
			 := shortestDistance(.rect, .rect)
			if  >= 0 {
				return 
			}
			 := .rect.Center().Sub(.rect.Center()).Length()
			return -(overlap(.rect, .rect) - 1) * 
		}
	case CostMinimumRootDistance:
		.cost = func( *tEdge) float64 {
			return math.Min(
				.u.point.Distance(.preferredRoot.vertex),
				.v.point.Distance(.preferredRoot.vertex),
			)
		}
	default:
		return nil, fmt.Errorf("no SPOrE spanning-tree cost implementation for %q", graph.Get(&.Properties, SpanningTreeCost))
	}
	return , nil
}

func ( *importer) ( geometry.Point) bool {
	for ,  := range .graph.vertices {
		if .original.point ==  {
			return true
		}
	}
	return false
}

func ( *importer) ( *vertex) *node {
	for ,  := range .graph.vertices {
		if .original ==  {
			return 
		}
	}
	return nil
}

func ( *importer) () {
	.graph.edges = nil
	.graph.tree = nil
	for ,  := range .graph.vertices {
		.original = &vertex{point: .rect.Center()}
	}
}

func ( *importer) () {
	if len(.graph.vertices) == 0 {
		return
	}
	,  := math.Inf(1), math.Inf(1)
	,  := math.Inf(-1), math.Inf(-1)
	for ,  := range .graph.vertices {
		 := .model.Node(.origin)
		.Shape.SetLocation(.rect.X, .rect.Y)
		 = math.Min(, .Shape.X)
		 = math.Min(, .Shape.Y)
		 = math.Max(, .Shape.X+.Shape.Width)
		 = math.Max(, .Shape.Y+.Shape.Height)
	}

	 := graph.Get(&.model.Node(.parent).Properties, Padding)
	elkutil.ResizeNode(
		.model,
		.parent,
		-+.Horizontal(),
		-+.Vertical(),
		true,
		true,
	)
	elkutil.Translate(.model, .parent, -+.Left, -+.Top)
	.routeEdges()
}

func ( *importer) () {
	for ,  := range .model.Node(.parent).ContainedEdges {
		 := .model.Edge()
		if len(.Sources) == 0 || len(.Targets) == 0 {
			continue
		}
		if len(.Sections) == 0 {
			.model.AddSection()
		}
		 := .model.Section(.Sections[0])
		 := .model.Node(.model.ShapeNode(.Sources[0]))
		 := .model.Node(.model.ShapeNode(.Targets[0]))
		 := .Shape.Rectangle().Center()
		 := .Shape.Rectangle().Center()
		 = .Add(clipVector(.Sub(), .Shape.Width, .Shape.Height))
		 = .Add(clipVector(.Sub(), .Shape.Width, .Shape.Height))
		.Start, .End = , 
		.StartPresent, .EndPresent = true, true
	}
}

func runUnderlying( *graph.Model,  graph.NodeID,  core.ProgressMonitor,  *core.AlgorithmRegistry) error {
	 := &.Node().Properties
	,  := graph.TryGet(, UnderlyingLayoutAlgorithm)
	if ! ||  == nil {
		return nil
	}
	 := .BySuffix()
	if  == nil {
		return nil
	}
	 := .Create()
	if  == nil {
		return fmt.Errorf("underlying layout algorithm %q returned a nil provider", )
	}
	return .Layout(, , .Subtask(1))
}