package d2isometricimg

import (
	
	
)

type routeCaptionPoint struct{ x, z float64 }

const maxRouteCaptionHullVertices = 64

// Project parallel to the final orthographic camera onto one common board
// plane. Comparing these coordinates is equivalent to comparing screen-space
// polygons, independent of output size, crop, or supersampling.
func captionProjection( Vec) routeCaptionPoint {
	 := nativeViewDirection()
	return routeCaptionPoint{.X - .Y*.X/.Y, .Z - .Y*.Z/.Y}
}

// AvoidMesh reserves a completed component's actual projected geometry. Its
// structural silhouette and printed surfaces are separate obstacles: joining an
// outside caption to the body's hull would unnecessarily block the gap between
// them. Call after relief, outside-caption clearance and other transforms.
func ( *routeCaptionPlacer) ( []Triangle) {
	if len(.rects) >= maxRouteCaptionRects {
		return
	}
	var  []routeCaptionPoint
	var  [][]routeCaptionPoint
	 := make(map[*Material]int)
	for ,  := range  {
		if .Material == nil || .Material.Color.A == 0 {
			continue
		}
		 := [3]routeCaptionPoint{}
		 := true
		for ,  := range .V {
			if !captionFinite(.Position.X, .Position.Y, .Position.Z) {
				 = false
				break
			}
			[] = captionProjection(.Position)
		}
		if ! {
			continue
		}
		if .Material.Texture == nil {
			 = append(, [:]...)
			continue
		}
		,  := [.Material]
		if ! {
			 = len()
			[.Material] = 
			 = append(, nil)
		}
		[] = append([], [:]...)
	}
	.avoidProjectedHull()
	// Iteration follows mesh/material creation order, never map order.
	for ,  := range  {
		.avoidProjectedHull()
	}
}

func ( *routeCaptionPlacer) ( []routeCaptionPoint) {
	if len() < 3 || len(.rects) >= maxRouteCaptionRects {
		return
	}
	 := captionConvexHull()
	if len() < 3 {
		return
	}
	 := routeCaptionRect{left: math.Inf(1), front: math.Inf(1), right: math.Inf(-1), back: math.Inf(-1), hard: true, hull: }
	for ,  := range  {
		.left, .right = min(.left, .x), max(.right, .x)
		.front, .back = min(.front, .z), max(.back, .z)
	}
	// Bound narrow-phase work for complex imported artwork. Falling back to
	// the actual projected bounds is conservative and never misses a collision.
	if len() > maxRouteCaptionHullVertices {
		.hull = []routeCaptionPoint{{.left, .front}, {.right, .front}, {.right, .back}, {.left, .back}}
	}
	.left -= routeCaptionGap
	.right += routeCaptionGap
	.front -= routeCaptionGap
	.back += routeCaptionGap
	.reserve()
}

// Automatic captions also avoid crossing other wires. The reserved straight
// legs use finalized routed vertices, including the small crossing bridges.
// Keep room in the shared obstacle budget for the captions themselves.
func ( *routeCaptionPlacer) ( []Vec,  float64) {
	if  <= 0 || !captionFinite() {
		return
	}
	for  := 1;  < len() && len(.rects) < maxRouteCaptionRects/2; ++ {
		,  := captionProjection([-1]), captionProjection([])
		if !captionFinite(.x, .z, .x, .z) {
			continue
		}
		 := math.Hypot(.x-.x, .z-.z)
		if  <= 1e-9 {
			continue
		}
		// Coordinates are already projected, so Y remains zero here.
		.reserve(captionRect(labelSurface{center: nv((.x+.x)/2, 0, (.z+.z)/2), width: , depth: 2 * , angle: math.Atan2(.z-.z, .x-.x)}, true))
	}
}

func captionConvexHull( []routeCaptionPoint) []routeCaptionPoint {
	sort.Slice(, func(,  int) bool {
		return [].x < [].x || [].x == [].x && [].z < [].z
	})
	 := func(, ,  routeCaptionPoint) float64 { return (.x-.x)*(.z-.z) - (.z-.z)*(.x-.x) }
	 := make([]routeCaptionPoint, 0, min(len(), 64))
	for ,  := range  {
		for len() >= 2 && ([len()-2], [len()-1], ) <= 0 {
			 = [:len()-1]
		}
		 = append(, )
	}
	 := len()
	for  := len() - 2;  >= 0; -- {
		 := []
		for len() >  && ([len()-2], [len()-1], ) <= 0 {
			 = [:len()-1]
		}
		 = append(, )
	}
	return [:len()-1]
}

func captionHullCorners( routeCaptionRect) []routeCaptionPoint {
	if len(.hull) > 0 {
		return .hull
	}
	, , ,  := .ux*.hw, .uz*.hw, -.uz*.hd, .ux*.hd
	return []routeCaptionPoint{{.x -  - , .z -  - }, {.x +  - , .z +  - }, {.x +  + , .z +  + }, {.x -  + , .z -  + }}
}

func captionHullOverlap(,  routeCaptionRect) bool {
	,  := captionHullCorners(), captionHullCorners()
	 := func( []routeCaptionPoint, ,  float64) (float64, float64) {
		,  := math.Inf(1), math.Inf(-1)
		for ,  := range  {
			 := .x* + .z*
			,  = min(, ), max(, )
		}
		return , 
	}
	for ,  := range [][]routeCaptionPoint{, } {
		for ,  := range  {
			 := [(+1)%len()]
			,  := .z-.z, .x-.x
			 := math.Hypot(, )
			if  <= 1e-12 {
				continue
			}
			,  := (, , )
			,  := (, , )
			// Rectangles already include the print gap; mesh hulls need it.
			 := 0.
			if len(.hull) > 0 {
				 += routeCaptionGap * 
			}
			if len(.hull) > 0 {
				 += routeCaptionGap * 
			}
			if + <=  || + <=  {
				return false
			}
		}
	}
	return true
}