package hierarchy

import (
	
	

	
	
)

const (
	minHierarchyLevels = 3

	// Limit automatic workflow recognition to modest graphs. Long rank axes
	// and dense edge sets can exhaust geometry or placement budgets.
	maxAutomaticWorkflowNodes = 128
	maxAutomaticWorkflowEdges = 256
)

// Self-loops are routed, but they do not describe a relationship between
// hierarchy levels. Keep hierarchy detection and ordering consistent with the
// DAG ranker, which removes them before assigning levels.
func isHierarchyStructuralEdge( *layoutgraph.Edge) bool {
	return !.IsLoop()
}

func countHierarchyStructuralEdges( *layoutgraph.Node) int {
	 := 0
	for ,  := range .Edges {
		if isHierarchyStructuralEdge() {
			++
		}
	}
	return 
}

// AssignNodeHierarchy identifies hierarchical structures within the subgraphs of this graph
func ( context.Context,  *layoutgraph.Graph,  *layoutgraph.Node,  map[*layoutgraph.Node]struct{}) ( error) {
	if  := layoutgraph.Validate(, "AssignNodeHierarchy", );  != nil {
		return 
	}
	, ,  := layoutgraph.EnsureTransactionWorkGuard(, "AssignNodeHierarchyTransactions")
	if  != nil {
		return 
	}
	 := layoutgraph.NewGraphStateSnapshot(layoutgraph.GraphStateSnapshotOptions{
		CaptureTopology:   true,
		CaptureEdgeRoutes: true,
	})
	if  := .UpdateWithWorkGuard(, );  != nil {
		return 
	}
	 := &layoutgraph.Transaction{Graph: , PriorGraphState: }
	 := false
	defer func() {
		if ! {
			.Rollback()
		}
	}()
	,  := .OwnedNodes(, )
	if  != nil {
		return 
	}
	 := make(map[*layoutgraph.Hierarchy]struct{})
	for  := range  {
		if  := .Step();  != nil {
			return 
		}
		if .Hierarchy != nil {
			[.Hierarchy] = struct{}{}
		}
	}
	for  := range  {
		for  := range .Levels() {
			if  := .Step();  != nil {
				return 
			}
			if ,  := [];  {
				delete(.Levels(), )
			}
		}
	}
	for  := range  {
		if  := .Step();  != nil {
			return 
		}
		.Hierarchy = nil
	}
	if  := assign(, , , );  != nil {
		return 
	}
	if  := .Finish();  != nil {
		return 
	}
	 = true
	return nil
}

// RemoveIsolatedMemberships removes leaf nodes that inherited a container's
// hierarchy but have no edges of their own. They remain ordinary routing
// obstacles after hierarchy placement.
func ( *layoutgraph.Graph) {
	for ,  := range .Nodes {
		if .Hierarchy != nil && len(.Edges) == 0 && !.IsContainer() {
			delete(.Hierarchy.Levels(), )
			.Hierarchy = nil
		}
	}
}

func assign( context.Context,  *layoutgraph.Graph,  *layoutgraph.Node,  map[*layoutgraph.Node]struct{}) error {

	var  bool
	if  == nil {
		 = .IsRootHierarchy
	} else {
		 = .ForceHierarchy
	}

	 := layoutgraph.NewGraph()
	.CopyEntitiesFrom()

	for ,  := range .Containers[] {
		.AddNodeUnchecked()
	}
	 := .AbductEdges(, )
	 := false
	var  layoutgraph.NodeGraphOwnershipJournal
	defer func() {
		if ! {
			.RestoreEdgeAbductions()
		}
		// Edge reachability can include nodes outside hierarchyGraph.Nodes. Restore
		// those exact owners before retaining the assignment stage's existing owner
		// state for nodes in g.
		.Restore()
		layoutgraph.Nodes(.Nodes).SetGraphReference()
	}()

	for ,  := range .Containers[] {
		// if this container can't be in a hierarchy, it may have one inside it
		if ,  := []; ! && .IsContainer() {
			if  := (, , , );  != nil {
				return 
			}
		}
	}

	// does not consider Nears when splitting the subgraph
	, ,  := .SplitSubgraphsTracked(, layoutgraph.SplitOptions{}, nil)
	if  != nil {
		return 
	}
	for ,  := range  {
		,  := build(, , , , )
		if  != nil {
			return 
		}
		if  == nil {
			continue
		}
		for ,  := range .Nodes {
			.Hierarchy = 
			for ,  := range .AllDescendantNodes(, false) {
				.Levels()[] = .Levels()[]
				.Hierarchy = 
			}
		}
	}
	.RestoreEdgeAbductions()
	 = true

	// remove Nears of nodes in hierarchies
	if  == nil {
		for ,  := range .Nodes {
			 := .Nears
			.Nears = map[*layoutgraph.Node]struct{}{}
			if .Hierarchy == nil {
				for  := range  {
					if .Hierarchy == nil {
						.AddNear()
					}
				}
			}
		}
	}

	return nil
}

// buildHierarchy assigns a hierarchy level to each node in this graph given that all nodes can be in a hierarchy.
// | - Find sources (nodes with no incoming edges) and sinks (nodes without outgoing edges)
// | - If there is at least one of each
// | 	- Add sources to a queue
// | 	- Assign level 0 to sources
// | 	- Walk the queue in BFS manner and assign the level of adjacent nodes as the current node level + 1
// | 		- Keep the greater level assignment to avoid having connected nodes on the same row/level
// | 		- Only if the node wasn't already visited.
// | 		- If it was already visited, keep the previous level as it was used to compute other nodes level
//
// SQL-table-only subgraphs use the orthogonal table placement path rather than
// hierarchy placement, even when their edges form a directed hierarchy.
func build( context.Context,  *layoutgraph.Graph,  bool,  map[*layoutgraph.Node]struct{},  []*layoutgraph.EdgeAbduction) (*layoutgraph.Hierarchy, error) {
	if len(.Nodes) <= 1 {
		return nil, nil
	}
	 := 0
	 := 0
	 := true
	for ,  := range .Nodes {
		if ,  := []; ! {
			return nil, nil
		}
		 =  && .IsTable()

		// `if/else if` to ensure source and sink are different nodes
		if isSource() {
			++
		} else if isSink() {
			++
		}
	}

	if ! && ( ||  == 0 ||  == 0) {
		return nil, nil
	}

	 := layoutgraph.NewHierarchy()
	,  := makeSimpleDAG(, )
	if  != nil {
		return nil, 
	}
	,  := rankDAG(, )
	if  != nil {
		return nil, 
	}
	.ReplaceLevels(mapDAGToGraphLevels(, .nodeToLevel))
	.LevelCount = .levelCount
	if  || isValid(, , , , ) {
		return , nil
	}

	return nil, nil
}

// isValid prefers compact hierarchies with a dominant edge direction. Fully
// directed branched workflows retain their reading order even when they are
// too tall for the usual compactness heuristic.
func isValid( *layoutgraph.Hierarchy,  *layoutgraph.Graph,  []*layoutgraph.EdgeAbduction, ,  int) bool {
	if .LevelCount < minHierarchyLevels {
		return false
	}
	// a perfect square hierarchy would have nLevels and nLevels nodes on each level (a matrix nLevels x nLevels)
	// so, taking the ratio to nLevels * nLevels tries to approximate this estimate this hierarchy "squaredness"
	 := make(map[*layoutgraph.Node]struct{})
	for  := range .Levels() {
		[] = struct{}{}
		for ,  := range .AllDescendantNodes(, false) {
			[] = struct{}{}
		}
	}
	 := float64(len()) / float64(.LevelCount*.LevelCount)
	 :=  < 0.5
	 :=  > 2.0

	// At least 60% of the structural edges must point forward. The workflow
	// exception requires every structural edge to point forward.
	,  := countEdgeDirection(, )
	 := float64() >= 1.5*float64()
	 :=  == 0
	// More nodes than ranks requires a parallel rank: ordinary chains retain
	// their existing placement path. Count only top-level blocks here, since
	// descendants do not add parallel branches to the outer workflow.
	 := len(.Nodes) > .LevelCount &&  == 1 &&  == 1 &&  &&
		len() <= maxAutomaticWorkflowNodes && + <= maxAutomaticWorkflowEdges
	if  &&  && !minimumWorkflowExtentFits(, ) {
		 = false
	}

	// we want to avoid nodes with many edges, as they would create a lot of noisy routes
	 := make(map[*layoutgraph.Node]int)
	for ,  := range  {
		if !isHierarchyStructuralEdge(.Edge) {
			continue
		}
		if .OriginallyFrom != nil {
			[.CurrentFrom] += 1
		}
		if .OriginallyTo != nil {
			[.CurrentTo] += 1
		}
	}
	 := int(2 * math.Ceil(math.Sqrt(float64(len()))))
	 := false
	for  := range  {
		 := countHierarchyStructuralEdges() - []
		if  >  {
			 = true
			break
		}
	}

	return ! && (! || ) &&  && ! && !is1N1(, )
}

// minimumWorkflowExtentFits screens current rank dimensions with the minimum
// placement gaps. Container resizing can change these dimensions, and later
// padding can add space, so this does not guarantee that a layout will fit. It
// is called only for the bounded workflow exception, never for authored or
// ordinary hierarchies.
func minimumWorkflowExtentFits( *layoutgraph.Hierarchy,  *layoutgraph.Graph) bool {
	 := IsHorizontal(.Nodes)
	 := make([]float64, .LevelCount)
	for ,  := range .Levels() {
		 := .Height
		if  {
			 = .Width
		}
		[] = math.Max([], )
	}
	 := float64(.LevelCount-1) * (crossingSpacing + 2*layoutgraph.MinPortClearance)
	for ,  := range  {
		 += math.Ceil()
	}
	return  <= limits.MaxGraphSize
}

// countHierarchyEdgesDirections counts the direction of the edges in the given graph considering the node levels.
// Forward edges are the targeted ones coming (source) from top (lower level value) to (target) bottom (higher level value).
// Undirected edges and bidirectional edges are counted as backwardOrNeutral.
// Targeted edges coming from bottom to top are as backwardOrNeutral.
func countEdgeDirection( *layoutgraph.Hierarchy,  *layoutgraph.Graph) (int, int) {
	 := 0
	 := 0
	for ,  := range .Edges {
		if !isHierarchyStructuralEdge() {
			continue
		}
		++
		if !.IsDirected() {
			continue
		}
		, ,  := .DirectedEndpoints()
		if .Levels()[] > .Levels()[] {
			 += 1
		}
	}
	return ,  - 
}

// is1N1Hierarchy identifies if it has the format below
// .      ┌───────┐
// .      │       │
// .    ┌─┤       ├──┐
// .    │ │       │  │
// .    │ └───────┘  │
// . ┌──▼────┐   ┌───▼───┐
// . │       │   │       │
// . │       │   │       │
// . │       │   │       │
// . └──┬────┘   └────┬──┘
// .    │  ┌───────┐  │
// .    │  │       │  │
// .    └──►       ◄──┘
// .       │       │
// .       └───────┘
func is1N1( *layoutgraph.Hierarchy,  []*layoutgraph.EdgeAbduction) bool {
	if .LevelCount != 3 {
		return false
	}

	 := make(map[int][]*layoutgraph.Node)
	for ,  := range .Levels() {
		[] = append([], )
	}
	if len([0]) != 1 || len([2]) != 1 {
		return false
	}

	// This doesn't qualify
	// .┌──────────────────────────────────────────────────┐
	// .│                                                  │
	// .│                                                  │
	// .│                                                  │
	// .│       ┌────┐      ┌──────┐                       │
	// .│       │    │      │      │        ┌──────┐       │
	// .│       │    │      │      │        │      │       │
	// .│       └───┬┘      └─────┬┘        └────┬─┘       │
	// .│           │             │              │         │
	// .│           │             │              │         │
	// .└───────────┼─────────────┼──────────────┼─────────┘
	// .            │             │              │
	// .            │             │              │
	// .            │             │              │
	// .            │             │              │
	// .            │             │              │
	// .            ▼             │              │
	// .        ┌────────┐        │           ┌──▼───┐
	// .        │        │     ┌──▼──┐        │      │
	// .        │        │     │     │        │      │
	// .        └────┬───┘     └───┬─┘        └───┬──┘
	// .             │             │              │
	// .             │             │              │
	// .             │             │              │
	// .             │             │              │
	// .             │             │              │
	// .             │         ┌───▼───┐          │
	// .             │         │       │          │
	// .             └───────► │       │ ◄────────┘
	// .                       │       │
	// .                       └───────┘
	var  *layoutgraph.EdgeAbduction
	var  *layoutgraph.EdgeAbduction
	for ,  := range  {
		if !isHierarchyStructuralEdge(.Edge) {
			continue
		}
		if .CurrentFrom == [0][0] {
			if  != nil {
				if .OriginallyFrom != .OriginallyFrom {
					return false
				}
			} else {
				 = 
			}
		}
		if .CurrentTo == [0][0] {
			if  != nil {
				if .OriginallyTo != .OriginallyTo {
					return false
				}
			} else {
				 = 
			}
		}

		if .CurrentFrom == [2][0] {
			if  != nil {
				if .OriginallyFrom != .OriginallyFrom {
					return false
				}
			} else {
				 = 
			}
		}
		if .CurrentTo == [2][0] {
			if  != nil {
				if .OriginallyTo != .OriginallyTo {
					return false
				}
			} else {
				 = 
			}
		}
	}

	// all nodes on level 2 must be connected at least once above and once below
	for ,  := range [1] {
		 := false
		 := false
		for ,  := range .Edges {
			if !isHierarchyStructuralEdge() {
				continue
			}
			 := .Adjacent()
			if .Levels()[] == 0 {
				 = true
			} else if .Levels()[] == 2 {
				 = true
			} else {
				return false
			}
			if  &&  {
				break
			}
		}
		if ! || ! {
			return false
		}
	}

	// the source (level=0) can't be connected to the sink (level=2)
	 := [0][0]
	for ,  := range .Edges {
		if !isHierarchyStructuralEdge() {
			continue
		}
		 := .Adjacent()
		if .Levels()[] != 1 {
			return false
		}
	}

	return true
}

// makeSimpleDAG transforms the input into a DAG and removes duplicate edges
// - undirected (a -- b) and bidirectional (a <-> b) edges are split in 2: a -> b and b -> a
// - cycles are broken by reversing some edges
// - duplicate edges are combined, preserving one rank-weight unit per authored edge
// - loops (self-edges) are removed
func makeSimpleDAG( context.Context,  *layoutgraph.Graph) (*layoutgraph.Graph, error) {
	,  := limits.NewWorkGuard(, "MakeSimpleDAG", limits.MaxEngineWorkUnits)
	if  != nil {
		return nil, 
	}
	 := layoutgraph.NewGraph()
	 := func(,  *layoutgraph.Node,  int) {
		 := .Connect(, )
		.SourceArrowhead = layoutgraph.NoArrowhead
		.TargetArrowhead = layoutgraph.TriangleArrowhead
		.ID = layoutgraph.EntityID(len(.Edges))
		.SetHierarchyRankWeight()
	}

	 := make(map[layoutgraph.EntityID]*layoutgraph.Node)
	for ,  := range .Nodes {
		if  := .Step();  != nil {
			return nil, 
		}
		 := .AddNode(layoutgraph.NewNode(.ID, .Width, .Height))
		.D2ID = .D2ID
		[.ID] = 
	}

	for ,  := range .Edges {
		if  := .Step();  != nil {
			return nil, 
		}
		if !isHierarchyStructuralEdge() {
			continue
		}
		if .IsDirected() {
			, ,  := .DirectedEndpoints()
			([.ID], [.ID], 1)
		} else {
			// An undirected or bidirectional edge expands to opposing arcs so the
			// cycle breaker can choose a feasible orientation. The two arcs still
			// represent one authored edge, not two independent rank objectives.
			// They necessarily converge after cycle removal, so carry its unit
			// weight on one half and zero on the other before simplification.
			([.From.ID], [.To.ID], 1)
			([.To.ID], [.From.ID], 0)
		}
	}

	,  := findCycleEdges(, )
	if  != nil {
		return nil, 
	}
	if  := reverseEdges(, );  != nil {
		return nil, 
	}
	if  := removeDuplicateEdges(, );  != nil {
		return nil, 
	}
	// Preserve preparation's work charge and cancellation checkpoints after
	// simplifying the graph.
	for range .Nodes {
		if  := .Step();  != nil {
			return nil, 
		}
	}

	if  := .Finish();  != nil {
		return nil, 
	}
	return , nil
}

// findCycleEdges implements "A fast and effective heuristic for the feedback arc set problem" by P. Eades
// Here is an explanation of the algorithm https://youtu.be/Z0RGCWxvCxA?t=561
// A rough explanation
// 1. sources and sinks can't be in cycles, so remove them and flag their edges as non cycle
// 2. for the remaining nodes, take the node max(outgoing - incoming)
// 3. remove the node and flag the outgoing as non cycle
// 4. repeat from 1 until all nodes were processed
// return set(all edges) - set(non cycle edges)
func findCycleEdges( context.Context,  *layoutgraph.Graph) (map[*layoutgraph.Edge]struct{}, error) {
	,  := limits.NewWorkGuard(, "FindCycleEdges", limits.MaxEngineWorkUnits)
	if  != nil {
		return nil, 
	}
	 := make(map[*layoutgraph.Node]map[*layoutgraph.Edge]struct{})
	 := make(map[*layoutgraph.Node]map[*layoutgraph.Edge]struct{})
	 := func( *layoutgraph.Edge,  *layoutgraph.Node,  map[*layoutgraph.Node]map[*layoutgraph.Edge]struct{}) {
		if ,  := [];  {
			[] = struct{}{}
		} else {
			[] = map[*layoutgraph.Edge]struct{}{: {}}
		}
	}

	 := make(map[*layoutgraph.Node]struct{})
	// set of edges to take the difference with set of non cycle edges
	 := make(map[*layoutgraph.Edge]struct{})
	for ,  := range .Edges {
		if  := .Step();  != nil {
			return nil, 
		}
		[] = struct{}{}
		[.From] = struct{}{}
		[.To] = struct{}{}
		if .IsTargetedTo(.From) {
			(, .From, )
			(, .To, )
		} else {
			(, .From, )
			(, .To, )
		}
	}

	 := make(map[*layoutgraph.Edge]struct{})
	 := func( *layoutgraph.Node) error {
		for  := range [] {
			if  := .Step();  != nil {
				return 
			}
			[] = struct{}{}
			// update the adjacent node
			 := .Adjacent()
			 := []
			delete(, )
			// removes the isolated adjacent node
			if len() == 0 && len([]) == 0 {
				delete(, )
				delete(, )
				delete(, )
			}
		}
		for  := range [] {
			if  := .Step();  != nil {
				return 
			}
			[] = struct{}{}
			// update the adjacent node
			 := .Adjacent()
			 := []
			delete(, )
			// removes the isolated adjacent node
			if len() == 0 && len([]) == 0 {
				delete(, )
				delete(, )
				delete(, )
			}
		}
		delete(, )
		delete(, )
		delete(, )
		return nil
	}
	for len() > 0 {
		if  := .Step();  != nil {
			return nil, 
		}
		for {
			// as we delete sinks we might be creating new sinks
			// so this part runs until all sinks were removed
			 := false
			for  := range  {
				if  := .Step();  != nil {
					return nil, 
				}
				// only incoming edges = sink
				if len([]) == 0 {
					 = true
					if  := ();  != nil {
						return nil, 
					}
				}
			}
			if ! {
				break
			}
		}

		// isolated nodes are handled in `pruneNode` when we remove the edge of the adjacent node

		// sources
		for {
			 := false
			for  := range  {
				if  := .Step();  != nil {
					return nil, 
				}
				// only outgoing edges = source
				if len([]) == 0 {
					 = true
					if  := ();  != nil {
						return nil, 
					}
				}
			}
			if ! {
				break
			}
		}

		if len() > 0 {
			var  *layoutgraph.Node
			 := math.Inf(-1)
			for  := range  {
				if  := .Step();  != nil {
					return nil, 
				}
				 := float64(len([]) - len([]))
				if  >  {
					 = 
					 = 
				} else if  ==  {
					// prefer the node with more edges
					 := len([]) + len([])
					 := len([]) + len([])
					if  >  {
						 = 
						 = 
					} else if len([]) == len([]) && .ID < .ID {
						// if edge count is the same, prefer the smaller ID
						 = 
						 = 
					}
				}
			}
			for  := range [] {
				if  := .Step();  != nil {
					return nil, 
				}
				delete([], )
				delete([.Adjacent()], )
			}
			if  := ();  != nil {
				return nil, 
			}
		}
	}

	for  := range  {
		if  := .Step();  != nil {
			return nil, 
		}
		delete(, )
	}

	if  := .Finish();  != nil {
		return nil, 
	}
	return , nil
}

// ReverseEdges exchanges from, to = to, from
// arrowheads don't change because we want `a -> b` to become `b -> a` and not `b <- a`
func reverseEdges( context.Context,  map[*layoutgraph.Edge]struct{}) error {
	,  := limits.NewWorkGuard(, "ReverseEdges", limits.MaxEngineWorkUnits)
	if  != nil {
		return 
	}
	for range  {
		if  := .Step();  != nil {
			return 
		}
	}
	if  := .Finish();  != nil {
		return 
	}
	for  := range  {
		.From, .To = .To, .From
	}
	return nil
}

// removeDuplicateEdges combines multiple instances of the same edge (from -> to),
// summing their hierarchy rank weights.
// assumes all edges are directed
func removeDuplicateEdges( context.Context,  *layoutgraph.Graph) error {
	,  := limits.NewWorkGuard(, "RemoveDuplicateEdges", limits.MaxEngineWorkUnits)
	if  != nil {
		return 
	}
	 := make(map[*layoutgraph.Node]map[*layoutgraph.Node]*layoutgraph.Edge)
	 := make([]*layoutgraph.Edge, 0, len(.Edges))
	 := make(map[*layoutgraph.Edge]int)
	for ,  := range .Edges {
		if  := .Step();  != nil {
			return 
		}
		if ,  := [.From];  {
			if ,  := [.To];  {
				[] += .HierarchyRankWeight()
			} else {
				[.To] = 
				 = append(, )
				[] = .HierarchyRankWeight()
			}
		} else {
			[.From] = make(map[*layoutgraph.Node]*layoutgraph.Edge)
			[.From][.To] = 
			 = append(, )
			[] = .HierarchyRankWeight()
		}
	}

	if  := .Finish();  != nil {
		return 
	}
	for ,  := range  {
		.SetHierarchyRankWeight([])
	}
	.ReplaceEdgesUnchecked()
	return nil
}

func mapDAGToGraphLevels( *layoutgraph.Graph,  map[*layoutgraph.Node]int) map[*layoutgraph.Node]int {
	 := make(map[layoutgraph.EntityID]*layoutgraph.Node)
	for ,  := range .Nodes {
		[.ID] = 
	}

	 := make(map[*layoutgraph.Node]int)
	for ,  := range  {
		[[.ID]] = 
	}
	return 
}