package d2isometricimg

import (
	
	
	
	

	
)

const (
	nativeLaneMaxPoints = 100000
	nativeLaneMaxWork   = 25000000
	nativeLaneMaxOffset = .55
	nativeLaneMinShared = .45
)

type nativeLaneKey struct{ x, z, normal, y int64 }
type nativeLaneDirection struct{ x, z, y int64 }
type nativeLaneInterval struct{ lo, hi float64 }
type nativeLaneSegment struct {
	edge, part int
	a, b       Vec
	u, normal  Vec
	lo, hi     float64
	line       float64
	length     float64
	key        nativeLaneKey
	shared     []nativeLaneInterval
}
type nativeLaneObstacle struct {
	id                       string
	header                   bool
	left, right, front, back float64
}
type nativeLaneWork struct {
	ctx       context.Context
	remaining int
}

func ( *nativeLaneWork) ( int) error {
	if  := .ctx.Err();  != nil {
		return 
	}
	if  > .remaining {
		return fmt.Errorf("isometric route lanes exceed work limit")
	}
	.remaining -= 
	return nil
}

// nativeLaneRoutes separates overlapping straight runs before corner rounding
// and bridge detection. It keeps all source ports and elevations, changes only
// the shared run and its short fan-out, and never mutates the scene. A bounded
// set of small offsets is tried in stable edge-ID order. A run without a clear
// lane retains its source geometry rather than crossing a component or moving
// an entire board. Common port stubs necessarily remain shared.
func nativeLaneRoutes( context.Context,  []d2isometric.Edge,  []d2isometric.Node,  []d2isometric.Board) ([][]Vec, error) {
	return nativeResolveLaneRoutes(, , , , nativeLaneMaxWork)
}

func nativeResolveLaneRoutes( context.Context,  []d2isometric.Edge,  []d2isometric.Node,  []d2isometric.Board,  int) ([][]Vec, error) {
	if  == nil {
		return nil, fmt.Errorf("isometric route lanes require a context")
	}
	 := nativeLaneWork{ctx: , remaining: }
	if len() > nativeLaneMaxPoints || len() > nativeLaneMaxPoints || len() > nativeLaneMaxPoints {
		return nil, fmt.Errorf("isometric route lanes exceed scene limit")
	}
	if  := .spend(len() + len() + len());  != nil {
		return nil, 
	}
	 := make([][]Vec, len())
	 := make([][]int, len())
	 := []nativeLaneSegment{}
	 := map[nativeLaneKey][]int{}
	 := map[nativeLaneDirection][]int{}
	 := map[string]bool{}
	 := 0
	for ,  := range  {
		if [.ID] {
			return nil, fmt.Errorf("isometric route lanes require unique edge IDs: %q", .ID)
		}
		[.ID] = true
		if len(.Points) > 10000 || len(.Points) > nativeLaneMaxPoints- {
			return nil, fmt.Errorf("isometric route lanes exceed point limit")
		}
		 += len(.Points)
		if  := .spend(len(.Points));  != nil {
			return nil, 
		}
		 := make([]Vec, 0, len(.Points))
		for ,  := range .Points {
			if !bridgeFinite(.X) || !bridgeFinite(.Y) || !bridgeFinite(.Z) {
				return nil, fmt.Errorf("isometric lane route %q has invalid coordinates", .ID)
			}
			if len() == 0 || nlen(nsub(, [len()-1])) > 1e-9 {
				 = append(, )
			}
		}
		[] = bridgeStraightRuns()
		[] = make([]int, max(0, len([])-1))
		for  := range [] {
			[][] = -1
		}
		if !bridgeVisible() {
			continue
		}
		for  := 1;  < len([]); ++ {
			,  := [][-1], [][]
			 := math.Hypot(.X-.X, .Z-.Z)
			if .Y != .Y ||  < nativeLaneMinShared {
				continue
			}
			 := nv((.X-.X)/, 0, (.Z-.Z)/)
			if .X < -1e-10 || math.Abs(.X) <= 1e-10 && .Z < 0 {
				 = nmul(, -1)
			}
			 := nv(-.Z, 0, .X)
			 := ndot(, )
			 := nativeLaneKey{int64(math.Round(.X * 1e8)), int64(math.Round(.Z * 1e8)), int64(math.Round( * 1e6)), int64(math.Round(.Y * 1e6))}
			,  := ndot(, ), ndot(, )
			if  >  {
				,  = , 
			}
			 := len()
			[][-1] = 
			 = append(, nativeLaneSegment{edge: , part:  - 1, a: , b: , u: , normal: , lo: , hi: , line: , length: , key: })
			[] = append([], )
			 := nativeLaneDirection{.x, .z, .y}
			[] = append([], )
		}
	}
	,  := nativeLaneObstacles(, )
	if  != nil {
		return nil, 
	}
	 := make([]nativeLaneKey, 0, len())
	for  := range  {
		 = append(, )
	}
	sort.Slice(, func(,  int) bool {
		,  := [], []
		if .x != .x {
			return .x < .x
		}
		if .z != .z {
			return .z < .z
		}
		if .y != .y {
			return .y < .y
		}
		return .normal < .normal
	})
	 := make([]float64, len())
	 := make([]bool, len())
	 := make([][]Vec, len())
	 := 0
	for ,  := range  {
		 := []
		sort.Slice(, func(,  int) bool {
			,  := [[]], [[]]
			if .lo != .lo {
				return .lo < .lo
			}
			if [.edge].ID != [.edge].ID {
				return [.edge].ID < [.edge].ID
			}
			return .part < .part
		})
		 := 0.
		for ,  := range  {
			 := &[]
			 = max(, nativeRouteRadius([.edge]))
			for ,  := range [+1:] {
				if  := .spend(1);  != nil {
					return nil, 
				}
				 := &[]
				if .lo >= .hi-nativeLaneMinShared {
					break
				}
				,  := max(.lo, .lo), min(.hi, .hi)
				if .edge == .edge || - < nativeLaneMinShared || math.Abs(.line-.line) > 1e-7 || .a.Y != .a.Y {
					continue
				}
				 := len(.shared) + len(.shared)
				.shared = nativeLaneUnion(.shared, nativeLaneInterval{, })
				.shared = nativeLaneUnion(.shared, nativeLaneInterval{, })
				 += len(.shared) + len(.shared) - 
				if  > nativeLaneMaxPoints {
					return nil, fmt.Errorf("isometric route lanes exceed overlap interval limit")
				}
			}
		}
		 := 3.3* + .035 // account for both visible paper casings
		sort.Slice(, func(,  int) bool {
			,  := [[]], [[]]
			if [.edge].ID != [.edge].ID {
				return [.edge].ID < [.edge].ID
			}
			return .part < .part
		})
		for ,  := range  {
			 := []
			if len(.shared) == 0 {
				[] = true
				continue
			}
			for  := 0;  <= 2*int(nativeLaneMaxOffset/); ++ {
				 := float64((+1)/2) * 
				if %2 == 0 {
					 = -
				}
				// Prefer the same physical side of an edge as it turns. A
				// canonical line normal alone flips sides at half of the bends.
				 *= nativeLaneSense()
				 := true
				for ,  := range [nativeLaneDirection{.x, .z, .y}] {
					if  := .spend(1);  != nil {
						return nil, 
					}
					 := []
					if  ==  || .edge == .edge || min(.hi, .hi)-max(.lo, .lo) < nativeLaneMinShared {
						continue
					}
					 := 1.65*(nativeRouteRadius([.edge])+nativeRouteRadius([.edge])) + .035
					if .key ==  {
						if [] && math.Abs(-[]) < -1e-9 {
							 = false
							break
						}
					} else {
						// Do not collapse an existing neighboring line, or swap
						// across it while fanning out from the original run.
						 := .line + []
						if  != 0 &&  > min(.line, .line+)- &&  < max(.line, .line+)+ {
							 = false
							break
						}
					}
				}
				if ! {
					continue
				}
				if  == 0 {
					break
				}
				 := nativeLanePath(, )
				if len() < 2 {
					continue
				}
				if  := .spend(len() * 8);  != nil {
					return nil, 
				}
				,  := nativeLaneClear(&, nativeRoundedRoute(), , [.edge])
				if  != nil {
					return nil, 
				}
				if ! {
					continue
				}
				if len() > nativeLaneMaxPoints- {
					return nil, fmt.Errorf("isometric route lanes exceed resolved point limit")
				}
				 += len()
				[], [] = , 
				break
			}
			[] = true
		}
	}
	if  := nativeLaneJoinCorners(&, , , , , , , );  != nil {
		return nil, 
	}
	for ,  := range  {
		if len() < 2 {
			continue
		}
		 := []Vec{[0]}
		for ,  := range [] {
			if  >= 0 && len([]) > 0 {
				 = append(, [][1:]...)
			} else {
				 = append(, [+1])
			}
		}
		if len() > 10000 {
			return nil, fmt.Errorf("isometric lane route exceeds 10000 control points")
		}
		[] = 
	}
	return , .Err()
}

// Overlap intervals arrive in ascending start order from the line sweep.
func nativeLaneUnion( []nativeLaneInterval,  nativeLaneInterval) []nativeLaneInterval {
	if len() > 0 && .lo <= [len()-1].hi+.4 {
		[len()-1].hi = max([len()-1].hi, .hi)
		return 
	}
	return append(, )
}

func nativeLanePath( nativeLaneSegment,  float64) []Vec {
	,  := .16, .22+math.Abs()*.7
	 := make([]nativeLaneInterval, 0, len(.shared))
	 := ndot(nsub(.b, .a), .u) > 0
	for ,  := range .shared {
		,  := .lo-.lo, .hi-.lo
		if ! {
			,  = .length-, .length-
		}
		,  = max(+, ), min(.length--, )
		if  <  {
			continue
		}
		 = append(, nativeLaneInterval{, })
	}
	sort.Slice(, func(,  int) bool { return [].lo < [].lo })
	 := []nativeLaneInterval{}
	for ,  := range  {
		if len() > 0 && .lo-[len()-1].hi < 2*+.1 {
			[len()-1].hi = max([len()-1].hi, .hi)
		} else {
			 = append(, )
		}
	}
	if len() == 0 {
		return nil
	}
	 := []Vec{.a}
	 := func(,  float64) {
		 := nadd(nlerp(.a, .b, /.length), nmul(.normal, ))
		.Y = .a.Y
		 = append(, )
	}
	for ,  := range  {
		(.lo-, 0)
		(.lo, )
		if .hi > .lo {
			(.hi, )
		}
		(.hi+, 0)
	}
	return append(, .b)
}

func nativeLaneObstacles( []d2isometric.Node,  []d2isometric.Board) ([]nativeLaneObstacle, error) {
	 := make([]nativeLaneObstacle, 0, len()+len())
	for ,  := range  {
		if .Container || .Opacity <= 0 {
			continue
		}
		if !bridgeFinite(.Position.X) || !bridgeFinite(.Position.Z) || !bridgeFinite(.Size.X) || !bridgeFinite(.Size.Z) || .Size.X < 0 || .Size.Z < 0 {
			return nil, fmt.Errorf("isometric lane obstacle %q has invalid bounds", .ID)
		}
		 = append(, nativeLaneObstacle{id: .ID, left: .Position.X - .Size.X/2 - .08, right: .Position.X + .Size.X/2 + .08, front: .Position.Z - .Size.Z/2 - .17, back: .Position.Z + .Size.Z/2 + .17})
	}
	for ,  := range  {
		if .Label == "" {
			continue
		}
		if !bridgeFinite(.Position.X) || !bridgeFinite(.Position.Z) || !bridgeFinite(.Size.X) || !bridgeFinite(.Size.Z) || !bridgeFinite(.HeaderDepth) || .Size.X < 0 || .Size.Z < 0 || .HeaderDepth < 0 {
			return nil, fmt.Errorf("isometric lane board %q has invalid bounds", .ID)
		}
		 = append(, nativeLaneObstacle{id: .ID, header: true, left: .Position.X - .Size.X/2 + .2, right: .Position.X + .Size.X/2 - .2, front: .Position.Z - .Size.Z/2 + .07, back: .Position.Z - .Size.Z/2 + .HeaderDepth})
	}
	return , nil
}

func nativeLaneClear( *nativeLaneWork,  []Vec,  []nativeLaneObstacle,  d2isometric.Edge) (bool, error) {
	 := 1.65*nativeRouteRadius() + .02
	for ,  := range  {
		// Only the connected exit/entry stub may touch its own module. A
		// later detour alongside that module is an ordinary obstacle check.
		,  := 0, len()
		if !.header && len() > 0 && len(.Points) > 0 {
			 := func( Vec) bool {
				return .X > .left- && .X < .right+ && .Z > .front- && .Z < .back+
			}
			if .id == .Source && [0] == .Points[0] && ([0]) {
				for  := 1;  < len(); ++ {
					if  := .spend(1);  != nil {
						return false, 
					}
					if !([]) {
						 = 
						break
					}
				}
			}
			if .id == .Target && [len()-1] == .Points[len(.Points)-1] && ([len()-1]) {
				for  := len() - 2;  >= 0; -- {
					if  := .spend(1);  != nil {
						return false, 
					}
					if !([]) {
						 =  + 1
						break
					}
				}
			}
		}
		for  := 1;  < len(); ++ {
			if  := .spend(1);  != nil {
				return false, 
			}
			if  <=  ||  >=  {
				continue
			}
			if nativeLaneHitsBox([-1], [], , ) {
				return false, nil
			}
		}
	}
	return true, nil
}

func nativeLaneSense( nativeLaneSegment) float64 {
	if ndot(nsub(.b, .a), .u) < 0 {
		return -1
	}
	return 1
}

type nativeLaneCorner struct{ x, y, z int64 }
type nativeLaneJoint struct{ before, after int }

// Preserve a lane through a shared bend when its two offset lines have a
// compact intersection and their ordering agrees with neighboring lanes.
// Unsafe joins keep the already checked fan-out rather than forming a miter
// spike, crossing a component, or exchanging the order of two wires.
func nativeLaneJoinCorners( *nativeLaneWork,  [][]Vec,  [][]int,  []nativeLaneSegment,  []float64,  [][]Vec,  []nativeLaneObstacle,  []d2isometric.Edge) error {
	 := func( Vec) nativeLaneCorner {
		return nativeLaneCorner{int64(math.Round(.X * 1e7)), int64(math.Round(.Y * 1e7)), int64(math.Round(.Z * 1e7))}
	}
	 := map[nativeLaneCorner][]nativeLaneJoint{}
	for ,  := range  {
		for  := 1;  < len()-1; ++ {
			if  := .spend(1);  != nil {
				return 
			}
			,  := [][-1], [][]
			if  >= 0 &&  >= 0 {
				 := ([])
				[] = append([], nativeLaneJoint{, })
			}
		}
	}
	 := make([]int, len())
	for  := range  {
		[] = 
	}
	sort.Slice(, func(,  int) bool { return [[]].ID < [[]].ID })
	for ,  := range  {
		for  := 1;  < len([])-1; ++ {
			if  := .spend(1);  != nil {
				return 
			}
			,  := [][-1], [][]
			if  < 0 ||  < 0 || [] == 0 || [] == 0 || len([]) < 4 || len([]) < 4 {
				continue
			}
			,  := [], []
			if .b != .a || .a.Y != .b.Y || []*nativeLaneSense()*[]*nativeLaneSense() <= 0 {
				continue
			}
			 := func( nativeLaneSegment,  bool) bool {
				 := (nativeLaneSense() > 0) == 
				for ,  := range .shared {
					if  && .hi >= .hi-1e-7 || ! && .lo <= .lo+1e-7 {
						return true
					}
				}
				return false
			}
			if  := .spend(len(.shared) + len(.shared));  != nil {
				return 
			}
			if !(, true) || !(, false) {
				continue
			}
			 := true
			for ,  := range [(.b)] {
				if  := .spend(1);  != nil {
					return 
				}
				if [.before].edge ==  {
					continue
				}
				,  := .before, .after
				if [].key == .key && [].key == .key {
					,  = , 
				}
				if [].key != .key || [].key != .key {
					continue
				}
				,  := ([]-[])*nativeLaneSense(), ([]-[])*nativeLaneSense()
				if * < -1e-9 {
					 = false
					break
				}
			}
			if ! {
				continue
			}
			,  := nunit(nsub(.b, .a)), nunit(nsub(.b, .a))
			 := .X*.Z - .Z*.X
			if math.Abs() < .35 {
				continue
			}
			,  := nadd(.b, nmul(.normal, [])), nadd(.a, nmul(.normal, []))
			 := nsub(, )
			 := nadd(, nmul(, (.X*.Z-.Z*.X)/))
			.Y = .b.Y
			if nlen(nsub(, .b)) > min(.85, 3*max(math.Abs([]), math.Abs([]))) {
				continue
			}
			,  := [], []
			,  := len()-2, 1
			for  >= 1 && math.Abs(ndot(nsub([], .a), .normal)-[]) > 1e-7 {
				--
			}
			for  < len()-1 && math.Abs(ndot(nsub([], .a), .normal)-[]) > 1e-7 {
				++
			}
			if  < 1 ||  >= len()-1 || ndot(nsub(, []), ) < .03 || ndot(nsub([], ), ) < .03 {
				continue
			}
			 := []Vec{[-1], [], , [], [+1]}
			,  := nativeLaneClear(, nativeRoundedRoute(), , [])
			if  != nil {
				return 
			}
			if ! {
				continue
			}
			[] = append(append([]Vec(nil), [:+1]...), )
			[] = append([]Vec{}, [:]...)
		}
	}
	return nil
}

func nativeLaneHitsBox(,  Vec,  nativeLaneObstacle,  float64) bool {
	,  := 0., 1.
	for ,  := range [][4]float64{{.X, .X - .X, .left - , .right + }, {.Z, .Z - .Z, .front - , .back + }} {
		if math.Abs([1]) < 1e-12 {
			if [0] <= [2]+1e-9 || [0] >= [3]-1e-9 {
				return false
			}
			continue
		}
		,  := ([2]-[0])/[1], ([3]-[0])/[1]
		if  >  {
			,  = , 
		}
		,  = max(, ), min(, )
		if - <= 1e-9 {
			return false
		}
	}
	return - > 1e-9
}