package routing

import 

// Keeps OVG edges as one set for vertical edges and other set of horizontal ones.
// Can quickly check for intersections/overlaps
type ovgEdgeSet struct {
	horizontalEdges map[float64][]OVGEdge
	verticalEdges   map[float64][]OVGEdge
	horizontals     []float64
	verticals       []float64
	edges           map[OVGEdge]struct{}
}

func newOvgEdgeSet() *ovgEdgeSet {
	return &ovgEdgeSet{
		horizontalEdges: make(map[float64][]OVGEdge, 16),
		verticalEdges:   make(map[float64][]OVGEdge, 16),
		horizontals:     make([]float64, 0, 16),
		verticals:       make([]float64, 0, 16),
		edges:           make(map[OVGEdge]struct{}, 16),
	}
}

// insertSortedFloat64 inserts a value into a sorted slice while maintaining order
// Uses binary search to find insertion point - O(log n) instead of O(n log n)
func ( *ovgEdgeSet) ( *[]float64,  float64) {
	 := sort.SearchFloat64s(*, )
	if  < len(*) && (*)[] ==  {
		return // Already exists
	}
	* = append(*, 0)
	copy((*)[+1:], (*)[:])
	(*)[] = 
}

// insertSortedEdge inserts an edge into a sorted edge slice while maintaining order
// Edges are sorted by their minimum coordinate (Y for vertical, X for horizontal)
func ( *ovgEdgeSet) ( *[]OVGEdge,  OVGEdge,  bool) {
	 := func( OVGEdge) float64 {
		if  {
			if .From.Y < .To.Y {
				return .From.Y
			}
			return .To.Y
		} else {
			if .From.X < .To.X {
				return .From.X
			}
			return .To.X
		}
	}

	 := ()

	// Binary search for insertion point
	 := sort.Search(len(*), func( int) bool {
		return ((*)[]) >= 
	})

	* = append(*, OVGEdge{})
	copy((*)[+1:], (*)[:])
	(*)[] = 
}

func ( *ovgEdgeSet) ( *OVGEdge) {
	if ,  := .edges[*];  {
		return
	}
	.edges[*] = struct{}{}

	if .isVertical() {
		if ,  := .verticalEdges[.From.X]; ! {
			.verticalEdges[.From.X] = make([]OVGEdge, 0, 16)
			.insertSortedFloat64(&.verticals, .From.X)
		}
		 := .verticalEdges[.From.X]
		.insertSortedEdge(&, *, true)
		.verticalEdges[.From.X] = 
	} else if .isHorizontal() {
		if ,  := .horizontalEdges[.From.Y]; ! {
			.horizontalEdges[.From.Y] = make([]OVGEdge, 0, 16)
			.insertSortedFloat64(&.horizontals, .From.Y)
		}
		 := .horizontalEdges[.From.Y]
		.insertSortedEdge(&, *, false)
		.horizontalEdges[.From.Y] = 
	}
}

// addGuarded reserves every sorted-slice shift before mutating the set. The
// duplicate lookup itself is also accounted, and a guard failure leaves the set
// unchanged.
func ( *ovgEdgeSet) ( *OVGEdge,  workBudget) error {
	if  := .step();  != nil {
		return 
	}
	if ,  := .edges[*];  {
		return nil
	}
	if .isVertical() {
		if ,  := .verticalEdges[.From.X]; ! {
			if  := .add(uint64(len(.verticals)) + 1);  != nil {
				return 
			}
		}
		if  := .add(uint64(len(.verticalEdges[.From.X])) + 1);  != nil {
			return 
		}
	} else if .isHorizontal() {
		if ,  := .horizontalEdges[.From.Y]; ! {
			if  := .add(uint64(len(.horizontals)) + 1);  != nil {
				return 
			}
		}
		if  := .add(uint64(len(.horizontalEdges[.From.Y])) + 1);  != nil {
			return 
		}
	}
	.add()
	return .check()
}

func ( *ovgEdgeSet) ( *OVGEdge,  workBudget) (bool, error) {
	return .intersectsWithChecked(, )
}

func ( *ovgEdgeSet) ( *OVGEdge,  workBudget) (bool, error) {
	 := func() error {
		if  == nil {
			return nil
		}
		return .step()
	}
	if .isHorizontal() {
		,  := .From.X, .To.X
		if  <  {
			,  = , 
		}

		// check for overlaps
		for ,  := range .horizontalEdges[.From.Y] {
			if  := ();  != nil {
				return false, 
			}
			if .From.X < .To.X {
				if  <= .From.X {
					// horizontalEdges is ordered by minX, so no following edges have a minX less than this one
					break
				}
				if .To.X <=  {
					continue
				}
			} else {
				if  <= .To.X {
					break
				}
				if .From.X <=  {
					continue
				}
			}
			if !.sharePoints() {
				return true, nil
			}
		}

		 := .From.Y
		// The axes are sorted. Charge the skipped prefix as before, but begin
		// geometry checks at the first line that can cross this segment.
		 := sort.Search(len(.verticals), func( int) bool { return !(.verticals[] < ) })
		if  := chargeSkippedRouteWork(, );  != nil {
			return false, 
		}
		// check for crossings
		for ,  := range .verticals[:] {
			if  := ();  != nil {
				return false, 
			}
			// when edge is horizontal, lineCoordinate is the X of vertical lines
			// so we filter out the vertical lines outside the span of edge as they won't intersect
			// similarly, it happens when edge is vertical and we filter the horizontal ones
			if  <  {
				continue
			}
			if  <  {
				break
			}
			for ,  := range .verticalEdges[] {
				if  := ();  != nil {
					return false, 
				}
				if .From.Y < .To.Y {
					if  < .From.Y {
						break
					}
					if .To.Y <  {
						continue
					}
				} else {
					if  < .To.Y {
						break
					}
					if .From.Y <  {
						continue
					}
				}
				if !.sharePoints() {
					return true, nil
				}
			}
		}
	} else if .isVertical() {
		,  := .From.Y, .To.Y
		if  <  {
			,  = , 
		}
		// check for overlaps
		for ,  := range .verticalEdges[.From.X] {
			if  := ();  != nil {
				return false, 
			}
			if .From.Y < .To.Y {
				if  <= .From.Y {
					// verticalEdges is ordered by minY, so no following edges have a minY less than this one
					break
				}
				if .To.Y <=  {
					continue
				}
			} else {
				if  <= .To.Y {
					break
				}
				if .From.Y <=  {
					continue
				}
			}
			if !.sharePoints() {
				return true, nil
			}
		}

		 := .From.X
		 := sort.Search(len(.horizontals), func( int) bool { return !(.horizontals[] < ) })
		if  := chargeSkippedRouteWork(, );  != nil {
			return false, 
		}
		for ,  := range .horizontals[:] {
			if  := ();  != nil {
				return false, 
			}
			if  <  {
				continue
			}
			if  <  {
				break
			}
			for ,  := range .horizontalEdges[] {
				if  := ();  != nil {
					return false, 
				}
				if .From.X < .To.X {
					if  < .From.X {
						break
					}
					if .To.X <  {
						continue
					}
				} else {
					if  < .To.X {
						break
					}
					if .From.X <  {
						continue
					}
				}
				if !.sharePoints() {
					return true, nil
				}
			}
		}
	}
	return false, nil
}

func ( *ovgEdgeSet) ( *OVGEdge,  workBudget) ([]OVGEdge, error) {
	return .overlappingEdgesChecked(, )
}

func ( *ovgEdgeSet) ( *OVGEdge,  workBudget) ([]OVGEdge, error) {
	var  []OVGEdge
	var  []OVGEdge
	if .isVertical() {
		 = .verticalEdges[.From.X]
	} else if .isHorizontal() {
		 = .horizontalEdges[.From.Y]
	}

	for ,  := range  {
		if  != nil {
			if  := .step();  != nil {
				return nil, 
			}
		}
		if intersects(.From.Point, .To.Point, .From.Point, .To.Point) {
			 = append(, )
		}
	}

	return , nil
}