// Copyright (c) 2010, 2015 Kiel University and others.
// Copyright (c) 2016 Kiel University and others.
// Copyright (c) 2017 Kiel University and others.
//
// SPDX-License-Identifier: EPL-2.0

package intermediate

import (
	

	
	basegraph 
	
)

// ProcessHierarchicalPortConstraints imposes fixed-order constraints on
// east/west external-port dummies and replaces fixed-side north/south dummies
// with the per-layer representatives expected by the later router.
func (
	 *layered.LGraphArena,
	 layered.LGraphID,
) error {
	processEastWestHierarchicalPortConstraints(, )
	processNorthSouthHierarchicalPortConstraints(, )
	return nil
}

func processEastWestHierarchicalPortConstraints( *layered.LGraphArena,  layered.LGraphID) {
	if !basegraph.Get(&.Graph().Properties, layered.PortConstraintsOption).IsOrderFixed() {
		return
	}
	 := .Graph().Layers
	if len() == 0 {
		return
	}
	processEastWestHierarchyLayer(, [0])
	processEastWestHierarchyLayer(, [len()-1])
}

func processEastWestHierarchyLayer( *layered.LGraphArena,  layered.LayerID) {
	 := append([]layered.LNodeID(nil), .Layer().Nodes...)
	sort.SliceStable(, func(,  int) bool {
		 := .Node([]).Type == layered.ExternalPortNode
		 := .Node([]).Type == layered.ExternalPortNode
		if  !=  {
			return 
		}
		if ! {
			return false
		}
		return basegraph.Get(&.Node([]).Properties, layered.PortRatioOrPosition) <
			basegraph.Get(&.Node([]).Properties, layered.PortRatioOrPosition)
	})
	var  *layered.LNodeID
	for ,  := range  {
		if .Node().Type != layered.ExternalPortNode {
			break
		}
		 := basegraph.Get(&.Node().Properties, layered.ExternalPortSide)
		if  != layered.WestSide &&  != layered.EastSide {
			continue
		}
		if  != nil {
			 := basegraph.Get(&.Node(*).Properties,
				layered.InLayerSuccessorConstraints)
			 = append(, )
			basegraph.Set(&.Node(*).Properties,
				layered.InLayerSuccessorConstraints, )
		}
		 := 
		 = &
	}
}

func processNorthSouthHierarchicalPortConstraints(
	 *layered.LGraphArena,
	 layered.LGraphID,
) {
	if !basegraph.Get(&.Graph().Properties, layered.PortConstraintsOption).IsSideFixed() {
		return
	}
	 := append([]layered.LayerID(nil), .Graph().Layers...)
	 := len()
	 := make([]map[layered.Origin]layered.LNodeID, +2)
	 := make([][]layered.LNodeID, +2)
	for  := range  {
		[] = make(map[layered.Origin]layered.LNodeID)
	}
	 := make([]layered.LNodeID, 0)
	for ,  := range  {
		for ,  := range append([]layered.LNodeID(nil), .Layer().Nodes...) {
			if isNorthSouthHierarchyDummy(, ) {
				 = append(, )
				continue
			}
			for ,  := range append([]layered.LEdgeID(nil), .NodeIncomingEdges()...) {
				 := .EdgeSourceNode()
				if !isNorthSouthHierarchyDummy(, ) {
					continue
				}
				 := hierarchyDummyOrigin(, )
				,  := [][]
				if ! {
					 = createNorthSouthHierarchyRepresentative(, , )
					[][] = 
					[] = append([], )
				}
				 := .Node().Ports[1]
				.SetEdgeSource(, &)
			}
			for ,  := range append([]layered.LEdgeID(nil), .NodeOutgoingEdges()...) {
				 := .EdgeTargetNode()
				if !isNorthSouthHierarchyDummy(, ) {
					continue
				}
				 := hierarchyDummyOrigin(, )
				 :=  + 2
				,  := [][]
				if ! {
					 = createNorthSouthHierarchyRepresentative(, , )
					[][] = 
					[] = append([], )
				}
				 := .Node().Ports[0]
				.SetEdgeTarget(, &)
			}
		}
	}
	for ,  := range  {
		if len() == 0 {
			continue
		}
		var  layered.LayerID
		switch {
		case  == 0:
			 = .AddLayer()
			.Graph().Layers = append([]layered.LayerID{}, .Graph().Layers...)
		case  == len()-1:
			 = .AddLayerToGraph()
		default:
			 = .Graph().Layers[-1]
		}
		for ,  := range  {
			.SetNodeLayer(, &)
		}
	}
	for ,  := range  {
		.SetNodeLayer(, nil)
	}
	basegraph.Set(&.Graph().Properties,
		layered.ExternalPortReplacedDummies, )
}

func isNorthSouthHierarchyDummy( *layered.LGraphArena,  layered.LNodeID) bool {
	if .Node().Type != layered.ExternalPortNode {
		return false
	}
	 := basegraph.Get(&.Node().Properties, layered.ExternalPortSide)
	return  == layered.NorthSide ||  == layered.SouthSide
}

func hierarchyDummyOrigin( *layered.LGraphArena,  layered.LNodeID) layered.Origin {
	if ,  := basegraph.TryGet(&.Node().Properties, layered.OriginalElement);  {
		return 
	}
	return layered.LNodeOrigin()
}

func createNorthSouthHierarchyRepresentative(
	 *layered.LGraphArena,
	 layered.LGraphID,
	 layered.LNodeID,
) layered.LNodeID {
	 := .AddNode()
	.Node().Properties.CopyFrom(&.Node().Properties)
	basegraph.Set(&.Node().Properties, layered.ExternalPortReplacedDummy, )
	basegraph.Set(&.Node().Properties, layered.PortConstraintsOption,
		layered.PortConstraintsFixedPosition)
	basegraph.Set(&.Node().Properties, layered.AlignmentOption, layered.AlignmentCenter)
	.Node().Type = layered.ExternalPortNode
	 := .AddPort()
	.SetPortNode(, &)
	.SetPortSide(, layered.WestSide)
	 := .AddPort()
	.SetPortNode(, &)
	.SetPortSide(, layered.EastSide)
	return 
}

// SizeHierarchicalPortDummies gives north/south representatives the staggered
// widths required to separate their vertical edge segments.
func ( *layered.LGraphArena,  layered.LGraphID) error {
	 := 2 * basegraph.Get(&.Graph().Properties,
		layered.SpacingEdgeEdgeBetweenLayers)
	for ,  := range append([]layered.LayerID(nil), .Graph().Layers...) {
		,  := make([]layered.LNodeID, 0), make([]layered.LNodeID, 0)
		for ,  := range .Layer().Nodes {
			if .Node().Type != layered.ExternalPortNode {
				continue
			}
			switch basegraph.Get(&.Node().Properties, layered.ExternalPortSide) {
			case layered.NorthSide:
				 = append(, )
			case layered.SouthSide:
				 = append(, )
			}
		}
		setHierarchyDummyWidths(, , true, )
		setHierarchyDummyWidths(, , false, )
	}
	return nil
}

func setHierarchyDummyWidths(
	 *layered.LGraphArena,
	 []layered.LNodeID,
	 bool,
	 float64,
) {
	,  := 0.0, 
	if ! {
		 =  * float64(len()-1)
		 = -
	}
	for ,  := range  {
		basegraph.Set(&.Node().Properties, layered.AlignmentOption, layered.AlignmentCenter)
		.Node().Size.X = 
		for ,  := range .NodePortsOnSide(, layered.EastSide) {
			.Port().Position.X = 
		}
		 += 
	}
}

// PositionHierarchicalPorts fixes east/west dummy y coordinates for fixed
// ratio and fixed-position external ports.
func ( *layered.LGraphArena,  layered.LGraphID) error {
	 := .Graph().Layers
	if len() > 0 {
		positionHierarchyLayerPorts(, , [0])
	}
	if len() > 1 {
		positionHierarchyLayerPorts(, , [len()-1])
	}
	return nil
}

func positionHierarchyLayerPorts(
	 *layered.LGraphArena,
	 layered.LGraphID,
	 layered.LayerID,
) {
	 := basegraph.Get(&.Graph().Properties, layered.PortConstraintsOption)
	if !.IsRatioFixed() && !.IsPositionFixed() {
		return
	}
	 := .GraphActualSize().Y
	for ,  := range append([]layered.LNodeID(nil), .Layer().Nodes...) {
		if .Node().Type != layered.ExternalPortNode {
			continue
		}
		 := basegraph.Get(&.Node().Properties, layered.ExternalPortSide)
		if  != layered.EastSide &&  != layered.WestSide {
			continue
		}
		 := basegraph.Get(&.Node().Properties, layered.PortRatioOrPosition)
		if  == layered.PortConstraintsFixedRatio {
			 *= 
		}
		 := basegraph.Get(&.Node().Properties, layered.PortAnchor)
		.Node().Position.Y =  - .Y
		.NodeBorderToContentCoordinates(, false, true)
	}
}

// ResizeHierarchicalNode finishes a child graph and transfers its actual size
// and external-port positions to the compound node that owns it.
func ( *layered.LGraphArena,  layered.LGraphID) error {
	 := .Graph()
	for ,  := range append([]layered.LayerID(nil), .Layers...) {
		 := append([]layered.LNodeID(nil), .Layer().Nodes...)
		.LayerlessNodes = append(.LayerlessNodes, ...)
		.Layer().Nodes = nil
		for ,  := range  {
			.Node().Layer = nil
		}
	}
	.Layers = nil
	resizeHierarchyGraph(, )
	if .ParentNode != nil {
		transferHierarchyGraphToNode(, , *.ParentNode)
	}
	return nil
}

func resizeHierarchyGraph( *layered.LGraphArena,  layered.LGraphID) {
	 := .Graph()
	 := .GraphActualSize()
	 := 
	if containsString(basegraph.Get(&.Properties, layered.NodeSizeConstraints), "MINIMUM_SIZE") {
		 := basegraph.Get(&.Properties, layered.NodeSizeMinimum)
		if .X <= 0 {
			.X = 20
		}
		if .Y <= 0 {
			.Y = 20
		}
		if .X > .X {
			.X = .X
		}
		if .Y > .Y {
			.Y = .Y
		}
	}
	 := basegraph.Get(&.Properties, layered.ContentAlignment)
	if .X > .X {
		if containsString(, "H_CENTER") {
			.Offset.X += (.X - .X) / 2
		} else if containsString(, "H_RIGHT") {
			.Offset.X += .X - .X
		}
	}
	if .Y > .Y {
		if containsString(, "V_CENTER") {
			.Offset.Y += (.Y - .Y) / 2
		} else if containsString(, "V_BOTTOM") {
			.Offset.Y += .Y - .Y
		}
	}
	 := basegraph.Get(&.Properties, layered.GraphProperties)
	if &layered.GraphPropertyExternalPorts != 0 &&
		(.X > .X || .Y > .Y) {
		for ,  := range .LayerlessNodes {
			if .Node().Type != layered.ExternalPortNode {
				continue
			}
			switch basegraph.Get(&.Node().Properties, layered.ExternalPortSide) {
			case layered.EastSide:
				.Node().Position.X += .X - .X
			case layered.SouthSide:
				.Node().Position.Y += .Y - .Y
			}
		}
	}
	.Size = geometry.Point{
		X: .X - .Padding.Left - .Padding.Right,
		Y: .Y - .Padding.Top - .Padding.Bottom,
	}
}

func transferHierarchyGraphToNode(
	 *layered.LGraphArena,
	 layered.LGraphID,
	 layered.LNodeID,
) {
	 := .Graph()
	for ,  := range .LayerlessNodes {
		,  := basegraph.TryGet(&.Node().Properties, layered.OriginalElement)
		if ! || .Kind != layered.OriginLPort {
			continue
		}
		 := .Port(.LPort)
		.Position = .ExternalPortPosition(, , .Size.X, .Size.Y)
		 := basegraph.Get(&.Node().Properties, layered.ExternalPortSide)
		.SetPortSide(.LPort, )
	}
	 := .GraphActualSize()
	 := basegraph.Get(&.Properties, layered.GraphProperties)
	if &layered.GraphPropertyExternalPorts != 0 {
		basegraph.Set(&.Node().Properties, layered.PortConstraintsOption,
			layered.PortConstraintsFixedPosition)
		 := .NodeGraph()
		 := basegraph.Get(&.Graph().Properties, layered.GraphProperties)
		basegraph.Set(&.Graph().Properties, layered.GraphProperties,
			|layered.GraphPropertyNonFreePorts)
		.ResizeNode(, , false, true)
	} else {
		.ResizeNode(, , true, true)
	}
}

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