// Copyright (c) 2010, 2018 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 
	
)

// HyperedgeDummyMerger joins adjacent long-edge dummies that belong to the
// same hyperedge, subject to the source/target label-dummy restrictions from
// ELK 0.8.1.
type HyperedgeDummyMerger struct{}

func (*HyperedgeDummyMerger) (
	 *layered.LGraphArena,
	 layered.LGraphID,
) error {
	identifyHyperedges(, )
	for ,  := range .Graph().Layers {
		 := .Layer().Nodes
		if len() == 0 {
			continue
		}
		var  layered.LNodeID
		var  layered.NodeType
		 := false
		for  := 0;  < len(); ++ {
			 := []
			 := .Node().Type
			if  == layered.LongEdgeNode &&  &&  == layered.LongEdgeNode {
				,  := checkHyperedgeMergeAllowed(, , )
				if  != nil {
					return 
				}
				if .allowMerge {
					if  := mergeHyperedgeDummyNodes(
						, , , .sameSource, .sameTarget,
					);  != nil {
						return 
					}
					 = append([:], [+1:]...)
					.Layer().Nodes = 
					--
					 = 
					 = 
				}
			}
			, ,  = , , true
		}
	}
	return nil
}

// MergeHyperedgeDummies is the function-shaped API used by the intermediate
// processor dispatcher.
func ( *layered.LGraphArena,  layered.LGraphID) error {
	return (&HyperedgeDummyMerger{}).Process(, )
}

type hyperedgeMergeState struct {
	allowMerge bool
	sameSource bool
	sameTarget bool
}

func checkHyperedgeMergeAllowed(
	 *layered.LGraphArena,
	,  layered.LNodeID,
) (hyperedgeMergeState, error) {
	 := .Node()
	 := .Node()
	if len(.Ports) == 0 || len(.Ports) == 0 {
		return hyperedgeMergeState{}, fmt.Errorf("layered: long-edge dummy has no ports")
	}
	,  := basegraph.TryGet(&.Properties, layered.LongEdgeSource)
	,  := basegraph.TryGet(&.Properties, layered.LongEdgeSource)
	,  := basegraph.TryGet(&.Properties, layered.LongEdgeTarget)
	,  := basegraph.TryGet(&.Properties, layered.LongEdgeTarget)
	 :=  &&  &&  == 
	 :=  &&  &&  == 
	 := basegraph.Get(&.Properties, layered.LongEdgeHasLabelDummies)
	 := basegraph.Get(&.Properties, layered.LongEdgeHasLabelDummies)
	if ! && ! {
		return hyperedgeMergeState{
			allowMerge: .Port(.Ports[0]).ID == .Port(.Ports[0]).ID,
			sameSource: ,
			sameTarget: ,
		}, nil
	}
	 := (! ||
		basegraph.Get(&.Properties, layered.LongEdgeBeforeLabelDummy)) &&
		(! || basegraph.Get(&.Properties, layered.LongEdgeBeforeLabelDummy))
	 := (! ||
		!basegraph.Get(&.Properties, layered.LongEdgeBeforeLabelDummy)) &&
		(! || !basegraph.Get(&.Properties, layered.LongEdgeBeforeLabelDummy))
	return hyperedgeMergeState{
		allowMerge:  &&  ||  && ,
		sameSource: ,
		sameTarget: ,
	}, nil
}

func mergeHyperedgeDummyNodes(
	 *layered.LGraphArena,
	,  layered.LNodeID,
	,  bool,
) error {
	 := .NodePortsOnSide(, layered.WestSide)
	 := .NodePortsOnSide(, layered.EastSide)
	if len() == 0 || len() == 0 {
		return fmt.Errorf("layered: long-edge merge target %d has no west/east ports", )
	}
	,  := [0], [0]
	for ,  := range .Node().Ports {
		for len(.Port().IncomingEdges) > 0 {
			 := .Port().IncomingEdges[0]
			.SetEdgeTarget(, &)
		}
		for len(.Port().OutgoingEdges) > 0 {
			 := .Port().OutgoingEdges[0]
			.SetEdgeSource(, &)
		}
	}
	if ! {
		basegraph.Unset(&.Node().Properties, layered.LongEdgeSource)
	}
	if ! {
		basegraph.Unset(&.Node().Properties, layered.LongEdgeTarget)
	}
	return nil
}

func identifyHyperedges( *layered.LGraphArena,  layered.LGraphID) {
	 := make([]layered.LPortID, 0)
	for ,  := range .Graph().Layers {
		for ,  := range .Layer().Nodes {
			 = append(, .Node().Ports...)
		}
	}
	for ,  := range  {
		.Port().ID = -1
	}
	 := 0
	for ,  := range  {
		if .Port().ID == -1 {
			identifyHyperedgeDFS(, , )
			++
		}
	}
}

func identifyHyperedgeDFS(
	 *layered.LGraphArena,
	 layered.LPortID,
	 int,
) {
	 := .Port()
	.ID = 
	for ,  := range .IncomingEdges {
		 := .Edge()
		if .Source != nil && .Port(*.Source).ID == -1 {
			(, *.Source, )
		}
	}
	for ,  := range .OutgoingEdges {
		 := .Edge()
		if .Target != nil && .Port(*.Target).ID == -1 {
			(, *.Target, )
		}
	}
	if .Node == nil || .Node(*.Node).Type != layered.LongEdgeNode {
		return
	}
	for ,  := range .Node(*.Node).Ports {
		if  !=  && .Port().ID == -1 {
			(, , )
		}
	}
}