package d2isometricimg

import (
	
	
	
	
	
	
)

// A large face's centroid can be nearer than a label on its far end, even
// though that label is above the face everywhere they overlap. Order complete
// source paint groups using depth at their actual projected intersections.
// This preserves group opacity and the existing order of substrate/decal ink.
func svgOrderPaintUnits( context.Context,  []*svgPaintUnit) error {
	,  := svgOrderPaintUnitsWithLimits(, , svgDefaultVisibilityLimits)
	return 
}

type svgPaintOrderStats struct{ relations, cyclicGroups, conflictingPairs int }

type svgOrderPolygon struct {
	points []svgPoint
	plane  svgDepthPlane
	box    svgBox
}

func svgOrderPaintUnitsWithLimits( context.Context,  []*svgPaintUnit,  svgVisibilityLimits) (svgPaintOrderStats, error) {
	var  svgPaintOrderStats
	 := &svgVisibilityBudget{ctx: , limits: }
	if len() > .faces {
		return , fmt.Errorf("isometric SVG paint order exceeds unit limit")
	}
	 := make([][]svgOrderPolygon, len())
	 := make([]svgVisibilityPolygon, len())
	 := make([]svgVisibilityFace, len())
	,  := 0, 0
	for ,  := range  {
		 := svgBox{math.Inf(1), math.Inf(1), math.Inf(-1), math.Inf(-1)}
		for ,  := range .batches {
			for ,  := range .polygons {
				if  := .spend(len() + 1);  != nil {
					return , 
				}
				,  := svgPolygonPlane()
				if ! {
					continue
				}
				 += len()
				++
				if  > .vertices ||  > .fragments {
					return , fmt.Errorf("isometric SVG paint order exceeds polygon limit")
				}
				 := svgPolygonBox()
				[] = append([], svgOrderPolygon{points: , plane: , box: })
				.minX, .minY = math.Min(.minX, .minX), math.Min(.minY, .minY)
				.maxX, .maxY = math.Max(.maxX, .maxX), math.Max(.maxY, .maxY)
			}
		}
		if len([]) > 0 {
			[] = svgVisibilityPolygon{box: , points: [][0].points}
			[].opaque = true // Index every paint unit, including alpha surfaces.
		}
	}
	 := make([]*svgOrderTree, len())
	for ,  := range  {
		var  error
		[],  = svgNewOrderTree(, )
		if  != nil {
			return , 
		}
	}
	,  := svgNewVisibilityGrid(, , )
	if  != nil {
		return , 
	}
	 := make([][]int, len())
	 := make([]int, len())
	for ,  := range  {
		if  := .spend(1);  != nil {
			return , 
		}
		if  == nil || len(.points) == 0 {
			continue
		}
		 := func( []int) error {
			if  := .spend(len());  != nil {
				return 
			}
			for ,  := range  {
				if  <=  || [] == +1 {
					continue
				}
				[] =  + 1
				if !.box.overlaps([].box) {
					continue
				}
				,  := svgOrderRelation([], [], [].first, [].first, )
				if  != nil {
					return 
				}
				if  == 3 {
					.conflictingPairs++
				}
				if &1 != 0 {
					[] = append([], )
					.relations++
				}
				if &2 != 0 {
					[] = append([], )
					.relations++
				}
				if .relations > min(.gridReferences, 1000000) {
					return fmt.Errorf("isometric SVG paint order exceeds relation limit")
				}
			}
			return nil
		}
		if , , , ,  := .cellsFor(.box);  {
			for  := ;  <= ; ++ {
				for  := ;  <= ; ++ {
					if  := (.cells[*.nx+]);  != nil {
						return , 
					}
				}
			}
			if  := (.large);  != nil {
				return , 
			}
		}
	}
	, ,  := svgOrderComponents(, )
	if  != nil {
		return , 
	}
	for ,  := range  {
		if len() > 1 {
			.cyclicGroups++
		}
		// A true intersection/cycle between indivisible source opacity groups has
		// no single correct painter order. Keep the native renderer's stable order
		// inside that component, without violating any external depth relation.
		sort.SliceStable(, func(,  int) bool { return svgOrderLess(, [], []) })
	}
	 := make([][]int, len())
	 := make([]int, len())
	for ,  := range  {
		for ,  := range  {
			,  := [], []
			if  ==  {
				continue
			}
			[] = append([], )
			[]++
		}
	}
	 := &svgOrderHeap{units: , components: }
	for ,  := range  {
		if  == 0 {
			heap.Push(, )
		}
	}
	 := make([]*svgPaintUnit, 0, len())
	for .Len() > 0 {
		if  := .spend(1);  != nil {
			return , 
		}
		 := heap.Pop().(int)
		for ,  := range [] {
			 = append(, [])
		}
		for ,  := range [] {
			[]--
			if [] == 0 {
				heap.Push(, )
			}
		}
	}
	if len() != len() {
		return , fmt.Errorf("isometric SVG paint order has invalid component graph")
	}
	copy(, )
	return , nil
}

// Return 1 for a→b, 2 for b→a, or 3 for a genuine crossing. Shared edges have
// no overlap area and impose no paint order. DepthBias is already in points.z.
// Index the visible fragments once per source paint group. A large board may
// have thousands of small openings after clipping; comparing every fragment
// with every fragment of each overlapping object would be quadratic.
type svgOrderTree struct {
	polygons []svgOrderPolygon
	indices  []int
	nodes    []svgOrderTreeNode
}
type svgOrderTreeNode struct {
	box                     svgBox
	start, end, left, right int
}

func svgNewOrderTree( []svgOrderPolygon,  *svgVisibilityBudget) (*svgOrderTree, error) {
	 := &svgOrderTree{polygons: , indices: make([]int, len())}
	for  := range .indices {
		.indices[] = 
	}
	var  func(int, int) (int, error)
	 = func(,  int) (int, error) {
		if  := .spend( -  + 1);  != nil {
			return 0, 
		}
		 := svgBox{math.Inf(1), math.Inf(1), math.Inf(-1), math.Inf(-1)}
		for ,  := range .indices[:] {
			 := [].box
			.minX, .minY = math.Min(.minX, .minX), math.Min(.minY, .minY)
			.maxX, .maxY = math.Max(.maxX, .maxX), math.Max(.maxY, .maxY)
		}
		 := len(.nodes)
		.nodes = append(.nodes, svgOrderTreeNode{box: , start: , end: , left: -1, right: -1})
		if - <= 8 {
			return , nil
		}
		if  := .spend(( - ) * bits.Len(uint(-)));  != nil {
			return 0, 
		}
		 := .maxX-.minX >= .maxY-.minY
		sort.Slice(.indices[:], func(,  int) bool {
			,  := .indices[+], .indices[+]
			,  := [].box, [].box
			,  := .minY+.maxY, .minY+.maxY
			if  {
				,  = .minX+.maxX, .minX+.maxX
			}
			if  ==  {
				return  < 
			}
			return  < 
		})
		 := ( + ) / 2
		,  := (, )
		if  != nil {
			return 0, 
		}
		,  := (, )
		if  != nil {
			return 0, 
		}
		.nodes[].left, .nodes[].right = , 
		return , nil
	}
	if len() > 0 {
		if ,  := (0, len());  != nil {
			return nil, 
		}
	}
	return , nil
}
func ( *svgOrderTree) ( svgBox,  *svgVisibilityBudget,  func(svgOrderPolygon) (bool, error)) (bool, error) {
	var  func(int) (bool, error)
	 = func( int) (bool, error) {
		if  := .spend(1);  != nil {
			return false, 
		}
		 := .nodes[]
		if !.overlaps(.box) {
			return false, nil
		}
		if .left >= 0 {
			,  := (.left)
			if  ||  != nil {
				return , 
			}
			return (.right)
		}
		for ,  := range .indices[.start:.end] {
			if  := .spend(1);  != nil {
				return false, 
			}
			 := .polygons[]
			if !.overlaps(.box) {
				continue
			}
			,  := ()
			if  ||  != nil {
				return , 
			}
		}
		return false, nil
	}
	if len(.nodes) == 0 {
		return false, nil
	}
	return (0)
}
func svgOrderRelation(,  *svgOrderTree, ,  int,  *svgVisibilityBudget) (int, error) {
	 := len(.polygons) > len(.polygons)
	if  {
		,  = , 
		,  = , 
	}
	 := 0
	for ,  := range .polygons {
		,  := .visit(.box, , func( svgOrderPolygon) (bool, error) {
			,  := svgOrderPolygonRelation(, , , , )
			 |= 
			return  == 3, 
		})
		if  != nil {
			return 0, 
		}
		if  {
			break
		}
	}
	if  {
		 = (&1)<<1 | (&2)>>1
	}
	return , nil
}
func svgOrderPolygonRelation(,  svgOrderPolygon, ,  int,  *svgVisibilityBudget) (int, error) {
	 := .points
	 := 1.
	if svgPolygonArea(.points) < 0 {
		 = -1
	}
	for ,  := range .points {
		if  := .spend(len() + 1);  != nil {
			return 0, 
		}
		 := .points[(+1)%len(.points)]
		 := svgEdgeDistance(, )
		, _ = svgSplitPolygon(, func( svgPoint) float64 { return  * () })
		if len() == 0 {
			return 0, nil
		}
	}
	, ,  := math.Inf(1), math.Inf(-1), 1.
	for ,  := range  {
		,  := .plane.at(), .plane.at()
		,  = math.Min(, -), math.Max(, -)
		 = math.Max(, math.Max(math.Abs(), math.Abs()))
	}
	 := math.Max(1e-9, *2e-14)
	if  >= - &&  <=  {
		if  <=  {
			return 1, nil
		}
		return 2, nil
	}
	 := 0
	if  >  {
		 |= 1
	}
	if  < - {
		 |= 2
	}
	return , nil
}

func svgOrderLess( []*svgPaintUnit, ,  int) bool {
	,  := [], []
	if .opaque != .opaque {
		return .opaque
	}
	if !.opaque && .depth != .depth {
		return .depth < .depth
	}
	if .first != .first {
		return .first < .first
	}
	return  < 
}

type svgOrderHeap struct {
	indices    []int
	units      []*svgPaintUnit
	components [][]int
}

func ( svgOrderHeap) () int { return len(.indices) }
func ( svgOrderHeap) (,  int) bool {
	return svgOrderLess(.units, .components[.indices[]][0], .components[.indices[]][0])
}
func ( svgOrderHeap) (,  int) { .indices[], .indices[] = .indices[], .indices[] }
func ( *svgOrderHeap) ( any)   { .indices = append(.indices, .(int)) }
func ( *svgOrderHeap) () any {
	 := len(.indices) - 1
	 := .indices[]
	.indices = .indices[:]
	return 
}

// Iterative Kosaraju keeps even long source-order chains off the call stack.
func svgOrderComponents( [][]int,  *svgVisibilityBudget) ([][]int, []int, error) {
	type  struct{ ,  int }
	 := make([][]int, len())
	for ,  := range  {
		for ,  := range  {
			[] = append([], )
		}
	}
	 := make([]bool, len())
	 := make([]int, 0, len())
	for  := range  {
		if [] {
			continue
		}
		[] = true
		 := []{{: }}
		for len() > 0 {
			if  := .spend(1);  != nil {
				return nil, nil, 
			}
			 := &[len()-1]
			if . == len([.]) {
				 = append(, .)
				 = [:len()-1]
				continue
			}
			 := [.][.]
			.++
			if ![] {
				[] = true
				 = append(, {: })
			}
		}
	}
	 := make([]int, len())
	for  := range  {
		[] = -1
	}
	var  [][]int
	for  := len() - 1;  >= 0; -- {
		 := []
		if [] >= 0 {
			continue
		}
		 := len()
		[] = 
		 := []int{}
		var  []int
		for len() > 0 {
			if  := .spend(1);  != nil {
				return nil, nil, 
			}
			 := [len()-1]
			 = [:len()-1]
			 = append(, )
			for ,  := range [] {
				if [] < 0 {
					[] = 
					 = append(, )
				}
			}
		}
		 = append(, )
	}
	return , , nil
}