package layoutgraph

// EdgeAbduction represents the temporary transfer of an edge from a descendant
// container to the ancestor child so node placement can operate on containers.
type EdgeAbduction struct {
	Edge *Edge

	OriginallyFrom *Node
	OriginallyTo   *Node

	CurrentFrom *Node
	CurrentTo   *Node
}

// ClusterRDFSOrder returns cluster vessels in reverse depth-first container order.
func ( *Graph) () []*Node {
	 := make([]*Node, 0, len(.Clusters))

	 := append(.containerRDFSOrder(nil), nil)
	for ,  := range  {
		for ,  := range .Containers[] {
			if .isClusterVessel {
				 = append(, )
			}
		}
	}

	return 
}

// ClusterOrder returns cluster vessels in stable ID order.
func ( *Graph) () []*Node {
	 := make([]*Node, 0, len(.Clusters))
	for  := range .Clusters {
		 = append(, )
	}
	sortNodesByID()
	return 
}

// TreeOrder returns tree sentinels in stable ID order.
func ( *Graph) () []*Node {
	 := make([]*Node, 0, len(.Trees))
	for  := range .Trees {
		 = append(, )
	}
	sortNodesByID()
	return 
}

// SequenceOrder returns sequence vessels in stable ID order.
func ( *Graph) () []*Node {
	 := make([]*Node, 0, len(.Sequences))
	for  := range .Sequences {
		 = append(, )
	}
	sortNodesByID()
	return 
}

func ( *Graph) ( []*EdgeAbduction) {
	for ,  := range  {
		if .OriginallyFrom != nil && .CurrentFrom != nil && .OriginallyFrom != .CurrentFrom {
			.Edge.reconnect(.CurrentFrom, false)
		}
		if .OriginallyTo != nil && .CurrentTo != nil && .OriginallyTo != .CurrentTo {
			.Edge.reconnect(.CurrentTo, true)
		}
	}
}

func ( *Graph) ( []*EdgeAbduction) []*EdgeAbduction {
	 := map[*Node]struct{}{}
	var  []*Node
	 = append(, .Nodes...)
	for len() > 0 {
		 := [0]
		 = [1:]
		[] = struct{}{}
		if .isContainer {
			 = append(, .Containers[]...)
		}
		if ,  := .Sequences[];  {
			 = append(, .Nodes...)
		}
		if .isClusterVessel {
			 = append(, .Clusters[].Nodes...)
		}
	}
	// Assign edges back to children
	var  []*EdgeAbduction
	for ,  := range  {
		,  := [.OriginallyFrom]
		if .OriginallyFrom == nil {
			_,  = [.CurrentFrom]
		}
		,  := [.OriginallyTo]
		if .OriginallyTo == nil {
			_,  = [.CurrentTo]
		}
		if ! || ! {
			 = append(, )
			continue
		}
		if .OriginallyFrom != nil {
			// If it is a cluster child, assign back to vessel
			if .OriginallyFrom.Cluster != nil {
				.OriginallyFrom = .OriginallyFrom.Cluster.Vessel
			}
			// If it is a sequence child, assign back to vessel
			if .OriginallyFrom.Sequence != nil {
				.OriginallyFrom = .OriginallyFrom.Sequence.Vessel
			}

			// If simple cluster abduction, edge didn't change
			if .CurrentFrom != .OriginallyFrom {
				.Edge.reconnect(.OriginallyFrom, false)
			}
		}
		if .OriginallyTo != nil {
			if .OriginallyTo.Cluster != nil {
				.OriginallyTo = .OriginallyTo.Cluster.Vessel
			}
			if .OriginallyTo.Sequence != nil {
				.OriginallyTo = .OriginallyTo.Sequence.Vessel
			}
			if .CurrentTo != .OriginallyTo {
				.Edge.reconnect(.OriginallyTo, true)
			}
		}
	}
	return 
}

// Edge abduction turns this graph
// .               ┌───────────────────┐
// .               │                   │
// . ┌───┐         │   ┌──┐            │
// . │   ├─────────┼──►│  ├─────┐      │
// . └───┘         │   └──┘     │      │
// .               │            ▼      │
// .   ┌────┐      │          ┌───┐    │
// .   │    │ ◄────┼──────────┤   │    │
// .   └────┘      │          └───┘    │
// .               │                   │
// .               └───────────────────┘
// . into this
// .               ┌───────────────────┐
// .               │                   │
// . ┌───┐         │   ┌──┐            │
// . │   ├────────►│   │  ├─────┐      │
// . └───┘         │   └──┘     │      │
// .               │            ▼      │
// .   ┌────┐      │          ┌───┐    │
// .   │    │◄─────┤          │   │    │
// .   └────┘      │          └───┘    │
// .               │                   │
// .               └───────────────────┘
// to make node placement work on containers instead of moving around container children
// But we still store data on these edge reassignments so that
// 1. we can revert back after node placement
// 2. we can resolve the true location of nodes within containers for median and edge length calculations
func ( *Graph) ( *Node,  *Graph) []*EdgeAbduction {
	 := []*EdgeAbduction{}
	for ,  := range .Clusters {
		 = append(, .EdgeAbductions...)
	}
	for ,  := range .Sequences {
		 = append(, .EdgeAbductions...)
	}

	 := make([]bool, len())
	 := []*EdgeAbduction{}
	// Find the edges which are connected to a descendant at one end and to a child at another end
	for ,  := range .Edges {
		 := false
		 := false

		 := false
		 := false

		var  *Node
		var  *Node

		for ,  := range .Containers[] {
			if  == .From {
				 = true
			} else if .From.isDescendantOf() {
				 = true
				 = 
			}
			if  == .To {
				 = true
			} else if .To.isDescendantOf() {
				 = true
				 = 
			}
		}

		if ( && (.From == )) || ( && (.To == )) {
			// This is an intra-container edge (points from a child into a descendant of child, or vice versa)
			// TODO (Mon Mar 28 21:06:30 2022) The ideal is perhaps having it prefer the outer borders, but that's a lot of work for a dubious use case
			// Do nothing special for now, just ignore the edge during placement and restore it at the end to avoid panics
			// Without this, both the inner node and container would consider each other for median placements
			// ┌──────────┐
			// │     ▲    │
			// │     │    │
			// │   ┌─┼─┐  │
			// │   └───┘  │
			// │          │
			// └──────────┘
			 = append(, &EdgeAbduction{
				Edge:           ,
				OriginallyTo:   .To,
				OriginallyFrom: .From,
			})
			.From.removeEdge()
			.To.removeEdge()
		} else if  &&  {
			.AddEdge()
			for ,  := range  {
				if [] {
					continue
				}
				if .CurrentFrom == .From && .CurrentTo == .To {
					 := &EdgeAbduction{
						Edge:        ,
						CurrentFrom: .From,
						CurrentTo:   .To,
					}
					if .OriginallyFrom != nil {
						.OriginallyFrom = .OriginallyFrom
					} else {
						.OriginallyTo = .OriginallyTo
					}
					[] = true
					 = append(, )
					break
				}
			}
		} else {
			// Else, if the edge is connected at one end to a child and another to a nested child, then temporarily reassign
			if  &&  {
				 = append(, &EdgeAbduction{
					Edge:           ,
					OriginallyFrom: .From,
					CurrentFrom:    ,
					CurrentTo:      .To,
				})
				.reconnect(, false)
				.AddEdge()
			} else if  &&  {
				 = append(, &EdgeAbduction{
					Edge:         ,
					OriginallyTo: .To,
					CurrentTo:    ,
					CurrentFrom:  .From,
				})
				.reconnect(, true)
				.AddEdge()
			} else if  &&  && ( != ) {
				// Edge can also be from one child descendant to another
				// But if they are the same ancestor, then leave it be
				 = append(, &EdgeAbduction{
					Edge:           ,
					OriginallyTo:   .To,
					OriginallyFrom: .From,
					CurrentTo:      ,
					CurrentFrom:    ,
				})
				.reconnect(, false)
				.reconnect(, true)
				.AddEdge()
			}
		}
	}

	// For every edge abduction, the current or original pointer at to or from can be a vessel
	for ,  := range  {
		for ,  := range  {
			if [] {
				continue
			}
			 := false

			if .OriginallyFrom == nil { // Must have abducted To
				if .CurrentFrom == .CurrentFrom && .OriginallyTo == .CurrentTo {
					 = true
				}
			} else if .OriginallyTo == nil { // Must have abducted From
				if .CurrentTo == .CurrentTo && .OriginallyFrom == .CurrentFrom {
					 = true
				}
			} else { // Must have abducted both
				if .OriginallyTo == .CurrentTo && .OriginallyFrom == .CurrentFrom {
					 = true
				}
			}
			if  {
				[] = true
				if .OriginallyFrom != nil {
					.OriginallyFrom = .OriginallyFrom
				} else {
					.OriginallyTo = .OriginallyTo
				}
				break
			}
		}
	}

	return 
}

func ( *Node) ( *Node) bool {
	if  ==  {
		return true
	}
	if  == nil {
		return false
	}
	if .Container != nil {
		return .Container.()
	}
	if .Cluster != nil {
		return .Cluster.Vessel.()
	}
	if .Sequence != nil {
		return .Sequence.Vessel.()
	}
	// Covers the case of nil ancestor
	return  == nil
}

// routes is provided when graph is still undergoing edge routing and the edges don't have points locked in yet.
func ( *Graph) () {
	for ,  := range .Nodes {
		.UpdateSpacing()
	}
}