// Copyright (c) 2008, 2020 Kiel University and others.
// Copyright (c) 2011, 2015 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 force

import (
	
	
	

	
	
	
	
	
)

// Importer is the Go counterpart of ELK 0.8.1's ElkGraphImporter. It retains
// arena IDs rather than Java origin-object properties.
type Importer struct {
	model  *graph.Model
	parent graph.NodeID
	nodes  map[graph.NodeID]*FNode
}

func ( *graph.Model,  graph.NodeID) *Importer {
	return &Importer{model: , parent: , nodes: make(map[graph.NodeID]*FNode)}
}

func ( *Importer) () (*FGraph, error) {
	 := .model.Node(.parent)
	 := &FGraph{
		Origin:                      .parent,
		Model:                       graph.Get(&.Properties, Model),
		Iterations:                  graph.Get(&.Properties, Iterations),
		Temperature:                 graph.Get(&.Properties, Temperature),
		Repulsion:                   graph.Get(&.Properties, Repulsion),
		SpacingNodeNode:             graph.Get(&.Properties, SpacingNodeNode),
		AspectRatio:                 graph.Get(&.Properties, AspectRatio),
		SeparateConnectedComponents: graph.Get(&.Properties, SeparateConnectedComponents),
		Interactive:                 graph.Get(&.Properties, Interactive),
		Padding:                     graph.Get(&.Properties, Padding),
	}
	 := graph.Get(&.Properties, RandomSeed)
	if  == 0 {
		// Java's new Random() is intentionally nondeterministic for seed zero.
		// The precise seed-uniquifier value is not stable across JVMs either.
		.Random = javacompat.NewRandom(time.Now().UnixNano())
	} else {
		.Random = javacompat.NewRandom(int64())
	}

	for ,  := range .Children {
		 := .model.Node()
		 := ""
		if len(.Labels) > 0 {
			 = .model.Label(.Labels[0]).Text
		}
		_ =  // Retained in Java for toString/debug output only.
		 := &FNode{
			Particle: Particle{
				Position: .Shape.Position().Add(geometry.Point{X: .Shape.Width / 2, Y: .Shape.Height / 2}),
				Size:     geometry.Point{X: math.Max(.Shape.Width, 1), Y: math.Max(.Shape.Height, 1)},
				Priority: graph.Get(&.Properties, Priority),
			},
			ID:     ,
			Origin: ,
		}
		.Particle.node = 
		.Nodes = append(.Nodes, )
		.nodes[] = 
	}

	// ElkGraphUtil.allOutgoingEdges includes edges connected through ports.
	// Iterating the model edge arena for each child preserves JSON/EMF order.
	for ,  := range .Children {
		for  := range .model.Edges {
			 := graph.EdgeID()
			 := .model.Edge()
			if len(.Sources) == 0 || .model.ShapeNode(.Sources[0]) !=  {
				continue
			}
			if len(.Sources) != 1 || len(.Targets) != 1 {
				return nil, fmt.Errorf("graph must not contain hyperedges")
			}
			 := .model.ShapeNode(.Targets[0])
			,  := .nodes[]
			,  := .nodes[]
			// Edges across hierarchy bounds and self loops are unsupported and
			// silently excluded by the Java importer.
			if ! || ! ||  ==  {
				continue
			}
			 := &FEdge{
				Origin:         ,
				Source:         ,
				Target:         ,
				Priority:       graph.Get(&.Properties, Priority),
				RepulsivePower: graph.Get(&.Properties, RepulsivePower),
			}
			.Edges = append(.Edges, )
			for ,  := range .Labels {
				 := .model.Label()
				 := &FLabel{
					Particle: Particle{
						Size:     geometry.Point{X: math.Max(.Shape.Width, 1), Y: math.Max(.Shape.Height, 1)},
						Priority: graph.Get(&.Properties, Priority),
					},
					Origin:  ,
					Edge:    ,
					Text:    .Text,
					Inline:  graph.Get(&.Properties, EdgeLabelsInline),
					Spacing: graph.Get(&.Properties, SpacingEdgeLabel),
				}
				.RefreshPosition()
				.Labels = append(.Labels, )
				.Labels = append(.Labels, )
			}
		}
	}
	return , nil
}

// ApplyLayout transfers FGraph coordinates back to the core graph, preserving
// ELK's mutation order (nodes are offset before edge endpoints are clipped).
func ( *Importer) ( *FGraph) {
	,  := float64(math.MaxInt32), float64(math.MaxInt32)
	,  := float64(math.MinInt32), float64(math.MinInt32)
	for ,  := range .Nodes {
		 = math.Min(, .Position.X-.Size.X/2)
		 = math.Min(, .Position.Y-.Size.Y/2)
		 = math.Max(, .Position.X+.Size.X/2)
		 = math.Max(, .Position.Y+.Size.Y/2)
	}
	for ,  := range .Bendpoints {
		 = math.Min(, .Position.X-.Size.X/2)
		 = math.Min(, .Position.Y-.Size.Y/2)
		 = math.Max(, .Position.X+.Size.X/2)
		 = math.Max(, .Position.Y+.Size.Y/2)
	}
	 := geometry.Point{X: .Padding.Left - , Y: .Padding.Top - }

	for ,  := range .Nodes {
		 := .model.Node(.Origin)
		 := .Position.Add()
		.Shape.SetLocation(.X-.Shape.Width/2, .Y-.Shape.Height/2)
	}
	for ,  := range .Edges {
		 := .model.Edge(.Origin)
		var  graph.SectionID
		if len(.Sections) == 0 {
			 = .model.AddSection(.Origin)
		} else {
			 = .Sections[0]
		}
		 := .model.Section()
		.Start = .SourcePoint().Add()
		.BendPoints = .BendPoints[:0]
		for ,  := range .Bendpoints {
			.BendPoints = append(.BendPoints, .Position.Add())
		}
		.End = .TargetPoint().Add()
		.StartPresent, .EndPresent = true, true
	}
	for ,  := range .Labels {
		 := .model.Label(.Origin)
		 := .Position.Add()
		.Shape.SetLocation(.X, .Y)
	}

	 :=  -  + .Padding.Horizontal()
	 :=  -  + .Padding.Vertical()
	if !graph.Get(&.model.Node(.parent).Properties, NodeSizeFixedGraphSize) {
		resizeNode(.model, .parent, , )
	}
	graph.Set(&.model.Node(.parent).Properties, core.ChildAreaWidth, -.Padding.Horizontal())
	graph.Set(&.model.Node(.parent).Properties, core.ChildAreaHeight, -.Padding.Vertical())
}

// resizeNode mirrors ElkUtil.resizeNode(node, width, height, false, true).
func resizeNode( *graph.Model,  graph.NodeID, ,  float64) {
	 := .Node()
	,  := .Shape.Width, .Shape.Height
	 := graph.Get(&.Properties, core.NodeSizeConstraints)
	if hasString(, "MINIMUM_SIZE") {
		 := graph.Get(&.Properties, core.NodeSizeMinimum)
		if hasString(graph.Get(&.Properties, nodespacing.NodeSizeOptions), "DEFAULT_MINIMUM_SIZE") {
			if .X <= 0 {
				.X = 20
			}
			if .Y <= 0 {
				.Y = 20
			}
		}
		 = math.Max(, .X)
		 = math.Max(, .Y)
	}
	,  := -, -
	.Shape.SetDimensions(, )
	for ,  := range .Labels {
		 := .Label()
		 := .Shape.X + .Shape.Width/2
		 := .Shape.Y + .Shape.Height/2
		 :=  / 
		 :=  / 
		if + >= 1 {
			if - > 0 &&  >= 0 {
				.Shape.X += 
				.Shape.Y +=  * 
				.Shape.Presence |= graph.HasX | graph.HasY
			} else if - < 0 &&  >= 0 {
				.Shape.X +=  * 
				.Shape.Y += 
				.Shape.Presence |= graph.HasX | graph.HasY
			}
		}
	}
	graph.Set(&.Properties, core.NodeSizeConstraints, []string(nil))
}

func hasString( []string,  string) bool {
	for ,  := range  {
		if  ==  {
			return true
		}
	}
	return false
}