// Copyright (c) 2015 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 intermediate

import (
	
	

	basegraph 
	
)

const interactiveExternalPortSpacing = 10

// PositionInteractiveExternalPorts assigns previous-layout coordinates to
// external-port dummies before the interactive Layered phases run.
func ( *layered.LGraphArena,  layered.LGraphID) error {
	 := .Graph()
	if basegraph.Get(&.Properties, layered.GraphProperties)&layered.GraphPropertyExternalPorts == 0 {
		return nil
	}
	,  := math.Inf(1), math.Inf(-1)
	,  := math.Inf(1), math.Inf(-1)
	for ,  := range .LayerlessNodes {
		 := .Node()
		if .Type != layered.NormalNode {
			continue
		}
		 = math.Min(, .Position.X-.Margin.Left)
		 = math.Max(, .Position.X+.Size.X+.Margin.Right)
		 = math.Min(, .Position.Y-.Margin.Top)
		 = math.Max(, .Position.Y+.Size.Y+.Margin.Bottom)
	}

	for ,  := range .LayerlessNodes {
		 := .Node()
		if .Type == layered.NormalNode {
			continue
		}
		if .Type != layered.ExternalPortNode {
			return fmt.Errorf("layered: node type %s is not supported by interactive external-port positioning", .Type)
		}
		switch basegraph.Get(&.Properties, layered.LayerConstraintOption) {
		case layered.LayerConstraintFirstSeparate:
			.Position.X =  - interactiveExternalPortSpacing
			if ,  := interactiveConnectedY(, , true);  {
				.Position.Y = 
			}
			continue
		case layered.LayerConstraintLastSeparate:
			.Position.X =  + interactiveExternalPortSpacing
			if ,  := interactiveConnectedY(, , false);  {
				.Position.Y = 
			}
			continue
		}
		switch basegraph.Get(&.Properties, layered.InternalInLayerConstraint) {
		case layered.InLayerConstraintTop:
			if , ,  := interactiveNorthSouthX(, );  != nil {
				return 
			} else if  {
				.Position.X =  + interactiveExternalPortSpacing
			}
			.Position.Y =  - interactiveExternalPortSpacing
		case layered.InLayerConstraintBottom:
			if , ,  := interactiveNorthSouthX(, );  != nil {
				return 
			} else if  {
				.Position.X =  + interactiveExternalPortSpacing
			}
			.Position.Y =  + interactiveExternalPortSpacing
		}
	}
	return nil
}

func interactiveConnectedY(
	 *layered.LGraphArena,
	 layered.LNodeID,
	 bool,
) (float64, bool) {
	for ,  := range .NodeConnectedEdges() {
		 := .EdgeSourceNode()
		if  {
			 = .EdgeTargetNode()
		}
		 := .Node()
		return .Position.Y + .Size.Y/2, true
	}
	return 0, false
}

func interactiveNorthSouthX(
	 *layered.LGraphArena,
	 layered.LNodeID,
) (float64, bool, error) {
	 := .Node().Ports
	if len() != 1 {
		return 0, false, fmt.Errorf("layered: interactive external-port dummy must have exactly one port")
	}
	 := .Port([0])
	if len(.OutgoingEdges) > 0 && len(.IncomingEdges) > 0 {
		return 0, false, fmt.Errorf("layered: interactive layout does not support north/south ports with incoming and outgoing edges")
	}
	if len(.OutgoingEdges) > 0 {
		 := math.Inf(1)
		for ,  := range .OutgoingEdges {
			 := .Node(.EdgeTargetNode())
			 = math.Min(, .Position.X-.Margin.Left)
		}
		return , true, nil
	}
	if len(.IncomingEdges) > 0 {
		 := math.Inf(-1)
		for ,  := range .IncomingEdges {
			 := .Node(.EdgeSourceNode())
			 = math.Max(, .Position.X+.Size.X+.Margin.Right)
		}
		return , true, nil
	}
	return 0, false, nil
}