// Copyright (c) 2010, 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 nodespacing

import (
	

	
	
	
)

// NodeMarginCalculator is a direct port of ELK v0.8.1's calculator. Its
// switches intentionally mutate the receiver, mirroring the Java fluent API.
type NodeMarginCalculator struct {
	includeLabels             bool
	includePorts              bool
	includePortLabels         bool
	includeEdgeHeadTailLabels bool
}

func () *NodeMarginCalculator {
	return &NodeMarginCalculator{
		includeLabels:             true,
		includePorts:              true,
		includePortLabels:         true,
		includeEdgeHeadTailLabels: true,
	}
}

func ( *NodeMarginCalculator) () *NodeMarginCalculator {
	.includeLabels = false
	return 
}

func ( *NodeMarginCalculator) () *NodeMarginCalculator {
	.includePorts = false
	return 
}

func ( *NodeMarginCalculator) () *NodeMarginCalculator {
	.includePortLabels = false
	return 
}

func ( *NodeMarginCalculator) () *NodeMarginCalculator {
	.includeEdgeHeadTailLabels = false
	return 
}

// Process calculates margins for every direct child of parent.
func ( *NodeMarginCalculator) ( *graph.Model,  graph.NodeID) {
	 := graph.Get(&.Node().Properties, SpacingLabelNode)
	for ,  := range .Node().Children {
		.processNode(, , )
	}
}

func ( *NodeMarginCalculator) ( *graph.Model, ,  graph.NodeID) {
	.processNode(, , graph.Get(&.Node().Properties, SpacingLabelNode))
}

func ( *NodeMarginCalculator) ( *graph.Model,  graph.NodeID,  float64) {
	 := .Node()
	 := .Shape.Position()
	 := geometry.Point{X: .Shape.Width, Y: .Shape.Height}
	 := .Shape.Rectangle()

	if .includeLabels {
		for ,  := range .Labels {
			 := .Label()
			 := .Shape.Rectangle().Move()
			 = .Union()
		}
	}

	for ,  := range .Ports {
		 := .Port()
		 := .Add(.Shape.Position())
		if .includePorts {
			 = .Union(.Shape.Rectangle().Move())
		}
		if .includePortLabels {
			for ,  := range .Labels {
				 = .Union(.Label().Shape.Rectangle().Move())
			}
		}
		if .includeEdgeHeadTailLabels {
			 := geometry.Point{X: -, Y: -}
			if has(graph.Get(&.Properties, PortLabelsPlacement), "OUTSIDE") {
				for ,  := range .Labels {
					 := .Label()
					.X += .Shape.Width + 
					.Y += .Shape.Height + 
				}
			}
			.X = math.Max(.X, 0)
			.Y = math.Max(.Y, 0)
			.processEdgeLabels(, &, .OutgoingEdges, false, , &, , )
			.processEdgeLabels(, &, .IncomingEdges, true, , &, , )
		}
	}

	if .includeEdgeHeadTailLabels {
		.processEdgeLabels(, &, .OutgoingEdges, false, , nil, geometry.Point{}, )
		.processEdgeLabels(, &, .IncomingEdges, true, , nil, geometry.Point{}, )
	}

	graph.Set(&.Properties, Margins, geometry.Spacing{
		Top:    math.Max(0, .Y-.Y),
		Right:  math.Max(0, .MaxX()-(.X+.X)),
		Bottom: math.Max(0, .MaxY()-(.Y+.Y)),
		Left:   math.Max(0, .X-.X),
	})
}

func ( *NodeMarginCalculator) (
	 *graph.Model,
	 *geometry.Rectangle,
	 []graph.EdgeID,
	 bool,
	 graph.NodeID,
	 *graph.PortID,
	 geometry.Point,
	 float64,
) {
	 := "TAIL"
	if  {
		 = "HEAD"
	}
	for ,  := range  {
		for ,  := range .Edge().Labels {
			 := .Label()
			if graph.Get(&.Properties, EdgeLabelsPlacement) !=  {
				continue
			}
			 := .edgeLabelBox(, , , , , , )
			* = .Union()
		}
	}
}

func ( *NodeMarginCalculator) (
	 *graph.Model,
	 graph.LabelID,
	 bool,
	 graph.NodeID,
	 *graph.PortID,
	 geometry.Point,
	 float64,
) geometry.Rectangle {
	 := .Node()
	 := .Label()
	 := geometry.Rectangle{X: .Shape.X, Y: .Shape.Y, Width: .Shape.Width, Height: .Shape.Height}
	if  == nil {
		if  {
			.X -=  + .Shape.Width
		} else {
			.X += .Shape.Width + 
		}
		return 
	}

	 := .Port(*)
	.X += .Shape.X
	.Y += .Shape.Y
	switch graph.Get(&.Properties, core.PortSide) {
	case "WEST":
		.X -=  + .X +  + .Shape.Width
	case "NORTH":
		.X += .Shape.Width + 
		.Y -=  + .Y +  + .Shape.Height
	case "SOUTH":
		.X += .Shape.Width + 
		.Y += .Shape.Height +  + .Y + 
	default: // UNDEFINED and EAST
		.X += .Shape.Width +  + .X + 
	}
	return 
}

func ( *graph.Model,  graph.NodeID,  bool) {
	 := NewNodeMarginCalculator()
	if  {
		.ExcludeEdgeHeadTailLabels()
	}
	.Process(, )
}