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

import (
	
	

	
	basegraph 
	
)

// PreprocessComments removes singly connected comment boxes from the normal
// layered pipeline and attaches them to the connected node for later placement.
func ( *layered.LGraphArena,  layered.LGraphID) error {
	 := append([]layered.LNodeID(nil), .Graph().LayerlessNodes...)
	for ,  := range  {
		if !basegraph.Get(&.Node().Properties, core.CommentBox) {
			continue
		}
		 := 0
		var  layered.LEdgeID
		var  layered.LPortID
		for ,  := range append([]layered.LPortID(nil), .Node().Ports...) {
			 := .Port()
			 += len(.IncomingEdges) + len(.OutgoingEdges)
			if len(.IncomingEdges) == 1 {
				 = .IncomingEdges[0]
				if .Edge().Source != nil {
					 = *.Edge().Source
				}
			}
			if len(.OutgoingEdges) == 1 {
				 = .OutgoingEdges[0]
				if .Edge().Target != nil {
					 = *.Edge().Target
				}
			}
		}
		if  == 1 && portDegree(, ) == 1 &&
			.Port().Node != nil &&
			!basegraph.Get(&.Node(*.Port().Node).Properties, core.CommentBox) {
			 := *.Port().Node
			processCommentBox(, , , , , )
			continue
		}

		// Comments that cannot be extracted stay regular nodes. Reverse oddly
		// connected incident edges in Java list order.
		var  []layered.LEdgeID
		for ,  := range append([]layered.LPortID(nil), .Node().Ports...) {
			for ,  := range append([]layered.LEdgeID(nil), .Port().OutgoingEdges...) {
				 := .Edge().Target
				if  != nil && len(.Port(*).OutgoingEdges) > 0 {
					 = append(, )
				}
			}
			for ,  := range append([]layered.LEdgeID(nil), .Port().IncomingEdges...) {
				 := .Edge().Source
				if  != nil && len(.Port(*).IncomingEdges) > 0 {
					 = append(, )
				}
			}
		}
		for ,  := range  {
			.ReverseEdge(, , true)
		}
	}
	return nil
}

func processCommentBox(
	 *layered.LGraphArena,
	 layered.LGraphID,
	 layered.LNodeID,
	 layered.LEdgeID,
	 layered.LPortID,
	 layered.LNodeID,
) {
	, ,  := true, false, false
	if basegraph.Get(&.Node().Properties, layered.PortConstraintsOption).IsSideFixed() {
		,  := false, false
	:
		for ,  := range .Node().Ports {
			for ,  := range connectedPorts(, ) {
				if .Port().Node == nil ||
					basegraph.Get(&.Node(*.Port().Node).Properties, core.CommentBox) {
					continue
				}
				switch .Port().Side {
				case layered.NorthSide:
					 = true
					break 
				case layered.SouthSide:
					 = true
					break 
				}
			}
		}
		 =  && !
		 =  && !
	}
	 := .Node().Labels
	if ! && ! && len() > 0 {
		 := 0.0
		for ,  := range  {
			 := .Label()
			 += .Position.Y + .Size.Y/2
		}
		 /= float64(len())
		 =  >= .Node().Size.Y/2
	} else {
		 = !
	}

	,  := basegraph.TryGet(&.Node().Properties, layered.TopComments)
	,  := basegraph.TryGet(&.Node().Properties, layered.BottomComments)
	 := false
	if  {
		switch {
		case !:
			 = true
		case :
			 = true
		case !:
			 = false
		default:
			 = len() <= len()
		}
	} else {
		switch {
		case !:
			 = false
		case :
			 = false
		case !:
			 = true
		default:
			 = len() < len()
		}
	}
	if  {
		basegraph.Set(&.Node().Properties, layered.TopComments,
			append(, ))
	} else {
		basegraph.Set(&.Node().Properties, layered.BottomComments,
			append(, ))
	}

	basegraph.Set(&.Node().Properties, layered.CommentConnectionPort, )
	if .Edge().Target != nil && *.Edge().Target ==  {
		.SetEdgeTarget(, nil)
		if portDegree(, ) == 0 {
			.SetPortNode(, nil)
		}
		removeHierarchicalCommentPortDummy(, )
	} else {
		.SetEdgeSource(, nil)
		if portDegree(, ) == 0 {
			.SetPortNode(, nil)
		}
	}
	.Edge().BendPoints = nil
	.Graph().LayerlessNodes = removeCommentNode(
		.Graph().LayerlessNodes, )
}

func connectedPorts( *layered.LGraphArena,  layered.LPortID) []layered.LPortID {
	 := make([]layered.LPortID, 0)
	for ,  := range .Port().OutgoingEdges {
		if .Edge().Target != nil {
			 = append(, *.Edge().Target)
		}
	}
	for ,  := range .Port().IncomingEdges {
		if .Edge().Source != nil {
			 = append(, *.Edge().Source)
		}
	}
	return 
}

func portDegree( *layered.LGraphArena,  layered.LPortID) int {
	 := .Port()
	return len(.IncomingEdges) + len(.OutgoingEdges)
}

func removeHierarchicalCommentPortDummy( *layered.LGraphArena,  layered.LPortID) {
	,  := basegraph.TryGet(&.Port().Properties, layered.PortDummy)
	if ! || .Node().Layer == nil {
		return
	}
	 := *.Node().Layer
	 := .Layer().Graph
	.SetNodeLayer(, nil)
	if len(.Layer().Nodes) != 0 {
		return
	}
	 := .Graph().Layers
	for ,  := range  {
		if  ==  {
			.Graph().Layers = append([:], [+1:]...)
			return
		}
	}
}

// CalculateCommentNodeMargins reserves the exact top, bottom, and horizontal
// protrusion required by comments extracted during preprocessing.
func ( *layered.LGraphArena,  layered.LGraphID) {
	for ,  := range .Graph().Layers {
		for ,  := range .Layer().Nodes {
			,  := basegraph.TryGet(&.Node().Properties, layered.TopComments)
			,  := basegraph.TryGet(&.Node().Properties, layered.BottomComments)
			if ! && ! {
				continue
			}
			 := layered.IndividualOrDefault(, , &core.SpacingCommentComment)
			 := layered.IndividualOrDefault(, , &core.SpacingCommentNode)
			,  := 0.0, 0.0
			if  {
				 := 0.0
				for ,  := range  {
					 := .Node()
					 = math.Max(, .Size.Y)
					 += .Size.X
				}
				 +=  * float64(len()-1)
				.Node().Margin.Top +=  + 
			}
			if  {
				 := 0.0
				for ,  := range  {
					 := .Node()
					 = math.Max(, .Size.Y)
					 += .Size.X
				}
				 +=  * float64(len()-1)
				.Node().Margin.Bottom +=  + 
			}
			 := math.Max(, )
			if  > .Node().Size.X {
				 := ( - .Node().Size.X) / 2
				.Node().Margin.Left = math.Max(.Node().Margin.Left, )
				.Node().Margin.Right = math.Max(.Node().Margin.Right, )
			}
		}
	}
}

// PostprocessComments reconnects extracted comment edges and puts the comment
// boxes back into their connected node's layer.
func ( *layered.LGraphArena,  layered.LGraphID) error {
	for ,  := range append([]layered.LayerID(nil), .Graph().Layers...) {
		var  []layered.LNodeID
		for ,  := range append([]layered.LNodeID(nil), .Layer().Nodes...) {
			,  := basegraph.TryGet(&.Node().Properties, layered.TopComments)
			,  := basegraph.TryGet(&.Node().Properties, layered.BottomComments)
			if ! && ! {
				continue
			}
			if  := placeComments(, , , , , );  != nil {
				return 
			}
			if  {
				 = append(, ...)
			}
			if  {
				 = append(, ...)
			}
		}
		for ,  := range  {
			.SetNodeLayer(, &)
		}
	}
	return nil
}

func placeComments(
	 *layered.LGraphArena,
	 layered.LNodeID,
	 []layered.LNodeID,
	 bool,
	 []layered.LNodeID,
	 bool,
) error {
	 := .Node()
	 := layered.IndividualOrDefault(, , &core.SpacingCommentComment)
	if  {
		,  := *float64(len()-1), 0.0
		for ,  := range  {
			 += .Node().Size.X
			 = math.Max(, .Node().Size.Y)
		}
		 := .Position.X - (-.Size.X)/2
		 := .Position.Y - .Margin.Top + 
		 := .Size.X / float64(len()+1)
		 := 
		for ,  := range  {
			 := .Node()
			.Position.X, .Position.Y = , -.Size.Y
			 += .Size.X + 
			, ,  := reconnectCommentEdge(, )
			if  != nil {
				return 
			}
			 := .Port()
			.Position.X = .Size.X/2 - .Anchor.X
			.Position.Y = .Size.Y
			if portDegree(, ) == 1 {
				 := .Port()
				.Position.X =  - .Anchor.X
				.Position.Y = 0
				.SetPortNode(, &)
			}
			 += 
		}
	}
	if  {
		,  := *float64(len()-1), 0.0
		for ,  := range  {
			 += .Node().Size.X
			 = math.Max(, .Node().Size.Y)
		}
		 := .Position.X - (-.Size.X)/2
		 := .Position.Y + .Size.Y + .Margin.Bottom - 
		 := .Size.X / float64(len()+1)
		 := 
		for ,  := range  {
			 := .Node()
			.Position.X, .Position.Y = , 
			 += .Size.X + 
			, ,  := reconnectCommentEdge(, )
			if  != nil {
				return 
			}
			 := .Port()
			.Position.X = .Size.X/2 - .Anchor.X
			.Position.Y = 0
			if portDegree(, ) == 1 {
				 := .Port()
				.Position.X =  - .Anchor.X
				.Position.Y = .Size.Y
				.SetPortNode(, &)
			}
			 += 
		}
	}
	return nil
}

func reconnectCommentEdge(
	 *layered.LGraphArena,
	 layered.LNodeID,
) (layered.LPortID, layered.LPortID, error) {
	,  := basegraph.TryGet(
		&.Node().Properties, layered.CommentConnectionPort)
	if ! {
		return 0, 0, fmt.Errorf("layered: comment box %d has no connection port", )
	}
	for ,  := range .Node().Ports {
		if  := .Port().OutgoingEdges; len() > 0 {
			 := [0]
			.SetEdgeTarget(, &)
			return , , nil
		}
		if  := .Port().IncomingEdges; len() > 0 {
			 := [0]
			.SetEdgeSource(, &)
			return , , nil
		}
	}
	return 0, 0, fmt.Errorf("layered: comment box %d has no connecting edge", )
}

func removeCommentNode( []layered.LNodeID,  layered.LNodeID) []layered.LNodeID {
	for ,  := range  {
		if  ==  {
			return append([:], [+1:]...)
		}
	}
	return 
}