// Copyright (c) 2012, 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 components

import (
	
	
	

	
	basegraph 
	
)

var (
	priority                      = basegraph.PropertyWithDefault("org.eclipse.elk.priority", func() int { return 0 })
	connectedComponentsCompaction = basegraph.PropertyWithDefault(
		"org.eclipse.elk.layered.compaction.connectedComponents", func() bool { return false })
)

// simpleRowGraphPlacer ports ELK 0.12.0's SimpleRowGraphPlacer and, when
// modelOrder is set, ModelOrderRowGraphPlacer.
type simpleRowGraphPlacer struct {
	modelOrder bool
}

func ( *simpleRowGraphPlacer) (
	 *layered.LGraphArena,
	 []layered.LGraphID,
	 layered.LGraphID,
) error {
	 := .Graph()
	if len() == 1 {
		 := [0]
		if  !=  {
			 := .Graph()
			.LayerlessNodes = nil
			moveGraph(, , , geometry.Point{})
			.Properties = .Properties.Clone()
			.Padding = .Padding
			.Size = .Size
		}
		return nil
	}
	if len() == 0 {
		.LayerlessNodes = nil
		.Size = geometry.Point{}
		return nil
	}
	for ,  := range  {
		if  ==  {
			return fmt.Errorf("components: target graph cannot be one of several components")
		}
	}

	if !.modelOrder && basegraph.Get(
		&.Properties, layered.ComponentOrdering,
	) == layered.ComponentOrderingNone {
		for ,  := range  {
			 := .Graph()
			 := 0
			for ,  := range .LayerlessNodes {
				 += basegraph.Get(&.Node().Properties, priority)
			}
			.ID = 
		}
		// Collections.sort is stable. The stable tie behavior is observable for
		// equal-size components and therefore must not be replaced by sort.Slice.
		sort.SliceStable(, func(,  int) bool {
			,  := .Graph([]), .Graph([])
			if .ID != .ID {
				return .ID > .ID
			}
			 := .Size.X * .Size.Y
			 := .Size.X * .Size.Y
			return  < 
		})
	}

	 := .Graph([0])
	.LayerlessNodes = nil
	.Properties = .Properties.Clone()

	,  := 0.0, 0.0
	for ,  := range  {
		 := .Graph().Size
		 = math.Max(, .X)
		 += .X * .Y
	}
	// The float cast in the Java source is intentional and survives GWT.
	 := float64(float32(math.Sqrt())) *
		basegraph.Get(&.Properties, layered.AspectRatio)
	 = math.Max(, )
	 := basegraph.Get(
		&.Properties, layered.SpacingComponentComponent)

	if .modelOrder {
		placeComponentsInModelOrder(
			, , , , )
	} else {
		placeComponentsInRows(, , , , )
	}

	if basegraph.Get(&.Properties, connectedComponentsCompaction) {
		,  := compactConnectedComponents(
			, , )
		if  != nil {
			return 
		}
		.Size = 
	}
	moveGraphs(, , , geometry.Point{})
	return nil
}

func placeComponentsInRows(
	 *layered.LGraphArena,
	 []layered.LGraphID,
	 *layered.LGraph,
	,  float64,
) {
	,  := 0.0, 0.0
	,  := 0.0, 
	for ,  := range  {
		 := .Graph()
		if +.Size.X >  {
			 = 0
			 +=  + 
			 = 0
		}
		offsetGraph(, , geometry.Point{
			X:  + .Offset.X,
			Y:  + .Offset.Y,
		})
		.Offset = geometry.Point{}
		 = math.Max(, +.Size.X)
		 = math.Max(, .Size.Y)
		 += .Size.X + 
	}
	.Size = geometry.Point{X: , Y:  + }
}

// placeComponentsInModelOrder is ELK 0.12.0's ModelOrderRowGraphPlacer.
// Components arrive sorted by their smallest node model order. External-port
// sides then constrain row breaks without reordering the components.
func placeComponentsInModelOrder(
	 *layered.LGraphArena,
	 []layered.LGraphID,
	 *layered.LGraph,
	,  float64,
) {
	,  := 0.0, 0.0
	,  := 0.0, 
	 := 0.0
	var  *layered.LGraph
	for ,  := range  {
		 := .Graph()
		 := basegraph.Get(&.Properties, layered.ExternalPortConnections)
		 :=  != nil && hasExternalPortSide(
			basegraph.Get(&.Properties, layered.ExternalPortConnections),
			layered.EastSide,
		)
		if +.Size.X >  &&
			!hasExternalPortSide(, layered.NorthSide) ||
			 || hasExternalPortSide(, layered.WestSide) {
			 = 
			 +=  + 
			 = 0
		}
		if hasExternalPortSide(, layered.NorthSide) {
			 =  + 
		}
		offsetGraph(, , geometry.Point{
			X:  + .Offset.X,
			Y:  + .Offset.Y,
		})
		 = math.Max(, +.Size.X)
		if hasExternalPortSide(, layered.SouthSide) {
			 = math.Max(, +.Size.X+)
		}
		.Offset = geometry.Point{}
		 = math.Max(, .Size.Y)
		 += .Size.X + 
		 = 
	}
	.Size = geometry.Point{X: , Y:  + }
}

func hasExternalPortSide( uint8,  layered.PortSide) bool {
	return &(uint8(1)<<uint8()) != 0
}