package d2elklayout

import 

// descendantShifter applies the same geometry updates as
// d2graph.Object.ShiftDescendants while avoiding its repeated whole-graph edge
// scans. ELK performs several sequential margin corrections per object, so the
// shifter deliberately keeps each correction separate: ShiftStart/ShiftEnd may
// simplify a route after each individual horizontal or vertical move.
type descendantShifter struct {
	preorder   map[*d2graph.Object]int
	subtreeEnd map[*d2graph.Object]int
	incident   map[*d2graph.Object][]*d2graph.Edge
	seen       map[*d2graph.Edge]uint64
	candidates []*d2graph.Edge
	generation uint64
}

func newDescendantShifter( *d2graph.Graph) *descendantShifter {
	 := &descendantShifter{
		preorder:   make(map[*d2graph.Object]int, len(.Objects)+1),
		subtreeEnd: make(map[*d2graph.Object]int, len(.Objects)+1),
		incident:   make(map[*d2graph.Object][]*d2graph.Edge, len(.Objects)),
		seen:       make(map[*d2graph.Edge]uint64, len(.Edges)),
	}
	 := 0
	var  func(*d2graph.Object)
	 = func( *d2graph.Object) {
		.preorder[] = 
		++
		for ,  := range .ChildrenArray {
			()
		}
		.subtreeEnd[] = 
	}
	(.Root)

	for ,  := range .Edges {
		.incident[.Src] = append(.incident[.Src], )
		if .Dst != .Src {
			.incident[.Dst] = append(.incident[.Dst], )
		}
	}
	return 
}

func ( *descendantShifter) (,  *d2graph.Object) bool {
	,  := .preorder[]
	,  := .preorder[]
	return  &&  &&  <=  &&  < .subtreeEnd[]
}

func ( *descendantShifter) ( *d2graph.Object, ,  float64) {
	if  == 0 &&  == 0 {
		return
	}

	.generation++
	if .generation == 0 {
		clear(.seen)
		.generation = 1
	}
	 := .generation
	 := .candidates[:0]
	 := func( *d2graph.Object) {
		for ,  := range .incident[] {
			if .seen[] ==  {
				continue
			}
			.seen[] = 
			 = append(, )
		}
	}

	// IsDescendantOf, used by the original implementation's first pass,
	// includes the object itself. Include root's incident edges so root self
	// loops and root-to-descendant edges preserve that behavior.
	()
	.IterDescendants(func( *d2graph.Object,  *d2graph.Object) {
		.TopLeft.X += 
		.TopLeft.Y += 
		()
	})
	.candidates = 

	for ,  := range  {
		 := .inSubtree(, .Src)
		 := .inSubtree(, .Dst)
		if  &&  {
			for ,  := range .Route {
				.X += 
				.Y += 
			}
			continue
		}

		// The original second pass visits strict descendants only. An edge
		// from root to an outside object therefore remains unchanged.
		if  && .Src !=  {
			shiftEdgeStart(, , )
		} else if  && .Dst !=  {
			shiftEdgeEnd(, , )
		}
	}
}

func shiftEdgeStart( *d2graph.Edge, ,  float64) {
	if  == 0 {
		.ShiftStart(, false)
	} else if  == 0 {
		.ShiftStart(, true)
	} else {
		.Route[0].X += 
		.Route[0].Y += 
	}
}

func shiftEdgeEnd( *d2graph.Edge, ,  float64) {
	if  == 0 {
		.ShiftEnd(, false)
	} else if  == 0 {
		.ShiftEnd(, true)
	} else {
		.Route[len(.Route)-1].X += 
		.Route[len(.Route)-1].Y += 
	}
}