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

import (
	
	

	
	
	
	
	
	basegraph 
)

type ElkGraphLayoutTransferrer struct {
	Model *basegraph.Model
	Maps  ImportMaps
}

func ( *basegraph.Model,  ImportMaps) *ElkGraphLayoutTransferrer {
	return &ElkGraphLayoutTransferrer{Model: , Maps: }
}

func ( *ElkGraphLayoutTransferrer) (
	 *LGraphArena,
	 LGraphID,
) error {
	if .Model == nil {
		return fmt.Errorf("layered: cannot transfer into a nil graph")
	}
	if  := .applyGraph(, , make(map[LGraphID]bool));  != nil {
		return 
	}
	 := make(map[basegraph.EdgeID]bool)
	for  := range .Edges {
		 := LEdgeID()
		,  := basegraph.TryGet(&.Edge().Properties, OriginalElement)
		if ! || .Kind != OriginEdge || [.Edge] {
			continue
		}
		[.Edge] = true
		.applyEdge(, , .Edge)
	}
	return nil
}

func ( *ElkGraphLayoutTransferrer) (
	 *LGraphArena,
	 LGraphID,
	 map[LGraphID]bool,
) error {
	if [] {
		return nil
	}
	[] = true
	 := .Graph()
	,  := basegraph.TryGet(&.Properties, OriginalElement)
	if ! || .Kind != OriginNode {
		return fmt.Errorf("layered: graph %d has no original parent node", )
	}
	 := .Offset.Add(geometry.Point{X: .Padding.Left, Y: .Padding.Top})
	 := make(map[LNodeID]bool)
	for ,  := range .GraphAllNodes() {
		if [] {
			continue
		}
		[] = true
		,  := basegraph.TryGet(&.Node().Properties, OriginalElement)
		if  && .Kind == OriginNode {
			.applyNode(, , .Node, )
		} else if  && .Kind == OriginPort && .ParentNode == nil {
			// A top-level external-port dummy represents a port on the node
			// whose contents are currently being laid out. Its position is
			// relative to that node rather than to a regular child node.
			 := .Model.Port(.Port)
			 := .ExternalPortPosition(
				, , .Shape.Width, .Shape.Height)
			.Shape.SetLocation(.X, .Y)
		}
		if .Node().NestedGraph != nil {
			if  := .(, *.Node().NestedGraph, );  != nil {
				return 
			}
		}
	}
	if .ParentNode == nil {
		.applyParentNodeLayout(, , .Node)
	}
	return nil
}

// applyParentNodeLayout mirrors ElkGraphLayoutTransferrer.applyParentNodeLayout.
// In particular, a graph without imported external-port dummies still has its
// ports moved by ElkUtil.resizeNode. This matters for isolated fixed-side ports
// on compound nodes whose dimensions change during their own layout run.
func ( *ElkGraphLayoutTransferrer) (
	 *LGraphArena,
	 LGraphID,
	 basegraph.NodeID,
) {
	 := .Graph()
	 := .Model.Node()
	 := basegraph.Get(&.Properties, core.NodeSizeConstraints)
	 := containsString(, "PORT_LABELS")

	 := .GraphActualSize()
	 := basegraph.Get(&.Properties, GraphProperties)
	if &GraphPropertyExternalPorts != 0 {
		// External-port dummies have already assigned final port positions.
		basegraph.Set(&.Properties, core.PortConstraints, "FIXED_POS")
		elkutil.ResizeNode(.Model, , .X, .Y, false, true)
	} else if !basegraph.Get(&.Properties, basicoptions.NodeSizeFixedGraphSize) {
		// No external-port dummy was imported, so resizing must adjust the
		// original node's peripheral ports exactly like ELK does.
		elkutil.ResizeNode(.Model, , .X, .Y, true, true)
	}

	if  {
		basegraph.Set(&.Properties, core.NodeSizeConstraints, []string{"PORT_LABELS"})
	} else {
		basegraph.Set(&.Properties, core.NodeSizeConstraints, []string{})
	}
}

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

func ( *ElkGraphLayoutTransferrer) (
	 *LGraphArena,
	 LNodeID,
	 basegraph.NodeID,
	 geometry.Point,
) {
	 := .Node()
	 := .Model.Node()
	.Shape.SetLocation(.Position.X+.X, .Position.Y+.Y)
	.Shape.SetDimensions(.Size.X, .Size.Y)
	basegraph.Set(&.Properties, LayerIDOption,
		func() int {
			if .Layer == nil {
				return -1
			}
			return .LayerIndex(*.Layer)
		}())
	basegraph.Set(&.Properties, PositionIDOption, .NodeIndexInLayer())

	for ,  := range .Ports {
		,  := basegraph.TryGet(&.Port().Properties, OriginalElement)
		if ! || .Kind != OriginPort {
			continue
		}
		 := .Port()
		 := .Model.Port(.Port)
		.Shape.SetLocation(.Position.X, .Position.Y)
		.Shape.SetDimensions(.Size.X, .Size.Y)
		basegraph.Set(&.Properties, core.PortSide, .Side.String())
		if !fixedPortLabelPlacement(&.Properties) {
			for ,  := range .Labels {
				.applyLabel(, , geometry.Point{})
			}
		}
	}
	 := nonemptyPlacement(&.Properties, nodespacing.NodeLabelsPlacement.ID)
	for ,  := range .Labels {
		if  || nonemptyPlacement(
			&.Label().Properties,
			nodespacing.NodeLabelsPlacement.ID,
		) {
			.applyLabel(, , geometry.Point{})
		}
	}
}

func nonemptyPlacement( *basegraph.PropertyMap,  string) bool {
	,  := .Raw()
	if ! {
		return false
	}
	switch typed := .(type) {
	case []string:
		return len() > 0
	case string:
		return strings.TrimSpace() != ""
	default:
		return false
	}
}

func fixedPortLabelPlacement( *basegraph.PropertyMap) bool {
	,  := .Raw(nodespacing.PortLabelsPlacement.ID)
	if ! {
		return false
	}
	switch typed := .(type) {
	case []string:
		for ,  := range  {
			if strings.EqualFold(, "FIXED") {
				return true
			}
		}
	case string:
		for ,  := range strings.FieldsFunc(, func( rune) bool {
			return  == ' ' ||  == ',' ||  == '[' ||  == ']'
		}) {
			if strings.EqualFold(, "FIXED") {
				return true
			}
		}
	}
	return false
}

func ( *ElkGraphLayoutTransferrer) (
	 *LGraphArena,
	 LLabelID,
	 geometry.Point,
) {
	.applyLabelInCoordinateSystem(, , , geometry.Point{})
}

func ( *ElkGraphLayoutTransferrer) (
	 *LGraphArena,
	 LLabelID,
	 geometry.Point,
	 geometry.Point,
) {
	,  := basegraph.TryGet(&.Label().Properties, OriginalElement)
	if ! || .Kind != OriginLabel {
		return
	}
	 := .Label()
	 := .Model.Label(.Label)
	.Shape.SetDimensions(.Size.X, .Size.Y)
	 := .Position.Add().Sub()
	.Shape.SetLocation(.X, .Y)
}

func ( *ElkGraphLayoutTransferrer) (
	 *LGraphArena,
	 LEdgeID,
	 basegraph.EdgeID,
) {
	 := .Edge()
	if .Source == nil || .Target == nil {
		return
	}
	 := .Model.Edge()
	if len(.Sources) == 0 || len(.Targets) == 0 {
		return
	}
	 := .EdgeSourceNode()
	 := .EdgeTargetNode()
	 := .IsDescendant(, ) &&
		.Node().NestedGraph != nil
	 := .NodeGraph()
	if  {
		 = *.Node().NestedGraph
	}
	,  := .edgeLayoutOffsets(, , )
	 := .Port(*.Source)
	 := .Port(*.Target)
	// LPort.getAbsoluteAnchor sums node, port, and anchor first. The layout
	// transferrer adds the graph offset afterwards. That evaluation order is
	// observable at the final ULP for fractional component and port positions.
	var  geometry.Point
	if  {
		// Descendant edges are laid out inside their source node. The source
		// anchor is already relative to that node; Java removes the base graph
		// offset before applying the complete edge offset below.
		 = .Position.Add(.Anchor).Sub()
	} else {
		 = .Node().Position.
			Add(.Position).
			Add(.Anchor)
	}
	 = .Add()
	 := .Node().Position.
		Add(.Position).
		Add(.Anchor)
	if ,  := basegraph.TryGet(&.Properties, TargetOffset);  {
		 = .Add()
	}
	 = .Add()
	// TARGET_OFFSET moves the target anchor into the edge's reference graph;
	// edgeOffset then converts that graph into the original edge container's
	// coordinate system.
	 := .Model.FirstSection(, true)
	 := .Model.Section()
	.Start = 
	.End = 
	.StartPresent, .EndPresent = true, true
	.IncomingShape = copyShapeID(&.Sources[0])
	.OutgoingShape = copyShapeID(&.Targets[0])
	.BendPoints = make(geometry.Chain, len(.BendPoints))
	for ,  := range .BendPoints {
		.BendPoints[] = .Add()
	}
	.BendPointsPresent = len(.BendPoints) > 0

	for ,  := range .Labels {
		.applyLabel(, , )
	}
	if ,  := basegraph.TryGet(&.Properties, JunctionPoints);  && len() > 0 {
		 := make(geometry.Chain, len())
		for ,  := range  {
			[] = .Add()
		}
		basegraph.Set(&.Properties, core.JunctionPoints, )
	} else {
		basegraph.Unset(&.Properties, core.JunctionPoints)
	}
}

func ( *ElkGraphLayoutTransferrer) (
	 *LGraphArena,
	 LEdgeID,
	 LGraphID,
) (geometry.Point, geometry.Point) {
	 := .Graph()
	 := .Offset.Add(
		geometry.Point{X: .Padding.Left, Y: .Padding.Top})
	 := geometry.Point{}
	,  := basegraph.TryGet(
		&.Edge().Properties,
		CoordinateSystemOrigin,
	)
	if  {
		 := .NodeGraph(.EdgeSourceNode())
		for  !=  {
			 := .Graph()
			if .ParentNode == nil {
				break
			}
			 := *.ParentNode
			 = .Add(
				.Node().Position)
			 = .NodeGraph()
			 := .Graph()
			 = .
				Add(.Offset).
				Add(geometry.Point{
					X: .Padding.Left,
					Y: .Padding.Top,
				})
		}
	}
	return , .Add()
}

func copyShapeID( *basegraph.ShapeID) *basegraph.ShapeID {
	if  == nil {
		return nil
	}
	 := *
	return &
}