package grouping

import (
	
	
	

	
	
)

const maxEngineNodes = limits.MaxEngineNodes

type clusterEdgeSignature struct {
	from, to                     int
	bidirectional, undirected    int
	directed                     int
	fromArrowheads, toArrowheads map[layoutgraph.Arrowhead]struct{}
}

func ( *clusterEdgeSignature) ( *layoutgraph.Node,  *layoutgraph.Edge) {
	if .From ==  {
		.from++
	}
	if .To ==  {
		.to++
	}
	switch {
	case .IsBidirectional():
		.bidirectional++
	case .IsUndirected():
		.undirected++
	case .IsDirected():
		.directed++
	}
	if .From ==  {
		.fromArrowheads[.SourceArrowhead] = struct{}{}
		.toArrowheads[.TargetArrowhead] = struct{}{}
	} else {
		.fromArrowheads[.TargetArrowhead] = struct{}{}
		.toArrowheads[.SourceArrowhead] = struct{}{}
	}
}

func ( clusterEdgeSignature) () int {
	 := 0
	if .directed > 0 {
		++
	}
	if .bidirectional > 0 {
		++
	}
	if .undirected > 0 {
		++
	}
	return 
}

func arrowheadSetsEqual(,  map[layoutgraph.Arrowhead]struct{}) bool {
	if len() != len() {
		return false
	}
	for  := range  {
		if ,  := []; ! {
			return false
		}
	}
	return true
}

func ( clusterEdgeSignature) ( clusterEdgeSignature) bool {
	return .from == .from &&
		.to == .to &&
		.bidirectional == .bidirectional &&
		.undirected == .undirected &&
		.arrowTypeCount() <= 1 &&
		.arrowTypeCount() <= 1 &&
		arrowheadSetsEqual(.fromArrowheads, .fromArrowheads) &&
		arrowheadSetsEqual(.toArrowheads, .toArrowheads)
}

type clusterDiscoveryInfo struct {
	neighbors       []*layoutgraph.Node
	neighborSet     map[*layoutgraph.Node]struct{}
	edges           []*layoutgraph.Edge
	edgeSignature   clusterEdgeSignature
	estimatedWidth  float64
	estimatedHeight float64
	noClustering    bool
	toTableColumn   bool
}

type clusterDiscoveryIndex struct {
	infos         map[*layoutgraph.Node]*clusterDiscoveryInfo
	edgeOrder     map[*layoutgraph.Edge]int
	edgeNodes     map[*layoutgraph.Edge][]*layoutgraph.Node
	sequenceEdges map[*layoutgraph.Sequence]map[*layoutgraph.Edge]*layoutgraph.Node
}

func ( *clusterDiscoveryIndex) (
	 *layoutgraph.Sequence,
	 *layoutgraph.Edge,
	 *limits.WorkGuard,
) (*layoutgraph.Node, error) {
	,  := .sequenceEdges[]
	if ! {
		 = make(map[*layoutgraph.Edge]*layoutgraph.Node, len(.EdgeAbductions))
		for ,  := range .EdgeAbductions {
			if  := .Step();  != nil {
				return nil, 
			}
			if  == nil || .Edge == nil {
				continue
			}
			switch {
			case .CurrentFrom == .Vessel:
				if ,  := [.Edge]; ! {
					[.Edge] = .OriginallyFrom
				}
			case .CurrentTo == .Vessel:
				if ,  := [.Edge]; ! {
					[.Edge] = .OriginallyTo
				}
			}
		}
		.sequenceEdges[] = 
	}
	return [], nil
}

func ( *clusterDiscoveryIndex) (
	 *layoutgraph.Graph,
	 *layoutgraph.Node,
	 *limits.WorkGuard,
) error {
	 := .infos[]
	if  == nil {
		return fmt.Errorf("TALA AddClusters cannot find node discovery index")
	}
	 := make([]*layoutgraph.Node, 0, len(.Edges))
	 := make(map[*layoutgraph.Node]struct{}, len(.Edges))
	for ,  := range .Edges {
		if  := .Step();  != nil {
			return 
		}
		 := .Adjacent()
		if  := .Sequences[];  != nil {
			var  error
			,  = .sequenceOriginal(, , )
			if  != nil {
				return 
			}
		}
		if ,  := []; ! {
			[] = struct{}{}
			 = append(, )
		}
	}
	.neighbors = 
	.neighborSet = 
	return .Finish()
}

func ( *clusterDiscoveryIndex) (
	 *layoutgraph.Graph,
	 *layoutgraph.Cluster,
	 []*layoutgraph.Edge,
	 *limits.WorkGuard,
) error {
	 := make([]*layoutgraph.Node, 0, len())
	 := make(map[*layoutgraph.Node]struct{}, len())
	 := func( *layoutgraph.Node) error {
		if  := .Step();  != nil {
			return 
		}
		if  == nil ||  == .Vessel || .infos[] == nil {
			return nil
		}
		if ,  := [];  {
			return nil
		}
		[] = struct{}{}
		 = append(, )
		return nil
	}
	// clusterIncidentEdges returns graph-edge order. Preserve it and the original
	// candidate-node discovery order. Using the reverse adjacency index also
	// preserves legacy behavior for tolerated asymmetric node.Edges inventories.
	for ,  := range  {
		for ,  := range .edgeNodes[] {
			if  := ();  != nil {
				return 
			}
		}
	}
	for ,  := range  {
		if  := .refreshNeighbors(, , );  != nil {
			return 
		}
	}
	return .Finish()
}

func clusterIsDescendantOfGuarded(,  *layoutgraph.Node,  *limits.WorkGuard) (bool, error) {
	 := make(map[*layoutgraph.Node]struct{})
	for {
		if  := .Step();  != nil {
			return false, 
		}
		if  ==  {
			return true, nil
		}
		if  == nil {
			return false, nil
		}
		if ,  := [];  {
			return false, fmt.Errorf("TALA AddClusters found a cycle in node ancestry")
		}
		if len() >= maxEngineNodes {
			return false, fmt.Errorf("TALA AddClusters ancestry exceeds node limit")
		}
		[] = struct{}{}
		switch {
		case .Container != nil:
			 = .Container
		case .Cluster != nil && .Cluster.Vessel != nil:
			 = .Cluster.Vessel
		case .Sequence != nil && .Sequence.Vessel != nil:
			 = .Sequence.Vessel
		default:
			return  == nil, nil
		}
	}
}

func clusterHasLeakyEdgeGuarded( *layoutgraph.Graph,  *layoutgraph.Node,  *limits.WorkGuard) (bool, error) {
	if !.IsContainer() {
		return false, nil
	}
	,  := .AllDescendantNodesWithWorkGuard(, true, )
	if  != nil {
		return false, 
	}
	for ,  := range  {
		if  := .Step();  != nil {
			return false, 
		}
		for ,  := range .Edges {
			if  := .Step();  != nil {
				return false, 
			}
			 := .Adjacent()
			,  := clusterIsDescendantOfGuarded(, , )
			if  != nil {
				return false, 
			}
			if ! {
				return true, nil
			}
		}
	}
	return false, .Finish()
}

func buildClusterDiscoveryIndex(
	 *layoutgraph.Graph,
	 []*layoutgraph.Node,
	 *limits.WorkGuard,
) (*clusterDiscoveryIndex, error) {
	 := make([]*layoutgraph.Node, 0, len(.Nodes))
	 := make(map[*layoutgraph.Node]struct{}, len(.Nodes))
	 := func( *layoutgraph.Node) error {
		if  := .Step();  != nil {
			return 
		}
		if  == nil {
			return fmt.Errorf("TALA AddClusters contains a nil node")
		}
		if ,  := [];  {
			return nil
		}
		if len() >= maxEngineNodes {
			return fmt.Errorf("TALA AddClusters unique node count exceeds limit %d", maxEngineNodes)
		}
		[] = struct{}{}
		 = append(, )
		return nil
	}
	for ,  := range  {
		if  := ();  != nil {
			return nil, 
		}
		for ,  := range .Containers[] {
			if  := ();  != nil {
				return nil, 
			}
		}
	}
	for ,  := range .Containers[nil] {
		if  := ();  != nil {
			return nil, 
		}
	}

	 := &clusterDiscoveryIndex{
		infos:         make(map[*layoutgraph.Node]*clusterDiscoveryInfo, len()),
		edgeOrder:     make(map[*layoutgraph.Edge]int, len(.Edges)),
		edgeNodes:     make(map[*layoutgraph.Edge][]*layoutgraph.Node, len(.Edges)),
		sequenceEdges: make(map[*layoutgraph.Sequence]map[*layoutgraph.Edge]*layoutgraph.Node),
	}
	for ,  := range  {
		.infos[] = &clusterDiscoveryInfo{
			neighborSet: make(map[*layoutgraph.Node]struct{}),
			edgeSignature: clusterEdgeSignature{
				fromArrowheads: make(map[layoutgraph.Arrowhead]struct{}),
				toArrowheads:   make(map[layoutgraph.Arrowhead]struct{}),
			},
			estimatedWidth:  .Width,
			estimatedHeight: .Height,
		}
	}

	for ,  := range  {
		 := .infos[]
		 := false
		for ,  := range .Edges {
			if  := .Step();  != nil {
				return nil, 
			}
			.edgeNodes[] = append(.edgeNodes[], )
			if .IsLoop() {
				 = true
			}
			if .FromTableColumnIndex != nil || .ToTableColumnIndex != nil {
				.toTableColumn = true
			}
		}
		if  := .refreshNeighbors(, , );  != nil {
			return nil, 
		}
		,  := clusterHasLeakyEdgeGuarded(, , )
		if  != nil {
			return nil, 
		}
		.noClustering = .IsTreeSentinel() ||
			.IsClusterVessel() ||
			.IsSequenceVessel() ||
			.IsTable() ||
			.Hierarchy != nil ||
			.FixedTopLeft != nil ||
			 ||
			
	}

	for ,  := range .Edges {
		if  := .Step();  != nil {
			return nil, 
		}
		if ,  := .edgeOrder[]; ! {
			.edgeOrder[] = 
		}
		if  := .infos[.From];  != nil {
			.edges = append(.edges, )
			.edgeSignature.add(.From, )
		}
		if .To != .From {
			if  := .infos[.To];  != nil {
				.edges = append(.edges, )
				.edgeSignature.add(.To, )
			}
		}
	}

	return , .Finish()
}

func clusterIncidentEdges(
	 *layoutgraph.Cluster,
	 map[*layoutgraph.Node]*clusterDiscoveryInfo,
	 map[*layoutgraph.Edge]int,
	 *limits.WorkGuard,
) ([]*layoutgraph.Edge, error) {
	 := make(map[*layoutgraph.Edge]struct{})
	for ,  := range .Nodes {
		if  := .Step();  != nil {
			return nil, 
		}
		 := []
		if  == nil {
			return nil, fmt.Errorf("TALA AddClusters cannot find cluster member index")
		}
		for ,  := range .edges {
			if  := .Step();  != nil {
				return nil, 
			}
			[] = struct{}{}
		}
	}
	 := make([]*layoutgraph.Edge, 0, len())
	for  := range  {
		 = append(, )
	}
	for  := 1;  < len();  *= 2 {
		for range  {
			if  := .Step();  != nil {
				return nil, 
			}
		}
		if  > len()/2 {
			break
		}
	}
	slices.SortFunc(, func(,  *layoutgraph.Edge) int {
		return cmp.Compare([], [])
	})
	return , .Finish()
}