package d2isometricimg

import (
	
	
	

	
	
	
)

// hierarchyBoardHeaderSurface places the existing print area at the compiled
// container label position. Outside and border labels are brought onto the
// physical surface; the container and its descendants are never resized.
func hierarchyBoardHeaderSurface( labelSurface,  d2isometric.Board,  d2isometric.Node,  []d2isometric.Node,  float64,  ...[]d2isometric.Board) labelSurface {
	,  := hierarchyBoardHeaderPlacement(, , , , , ...)
	return 
}

// The bool reports whether the full print area has an unoccluded placement.
// Prefer the original source edge, then the bottom edge and a bounded set of
// alternate strips. All candidates retain the dimensions, angle, and plane.
// If every candidate is blocked, return the original in-footprint anchor.
func hierarchyBoardHeaderPlacement( labelSurface,  d2isometric.Board,  d2isometric.Node,  []d2isometric.Node,  float64,  ...[]d2isometric.Board) (labelSurface, bool) {
	if !hierarchyHeaderPositive(.Size.X) || !hierarchyHeaderPositive(.Size.Z) || !hierarchyHeaderPositive(.width) || !hierarchyHeaderPositive(.depth) {
		return , false
	}
	if !hierarchyHeaderPositive() {
		 = d2isometric.SceneScale
	}
	,  := min(.05, .Size.X*.02), min(.05, .Size.Z*.02)
	,  := .Size.X-2*, .Size.Z-2*
	 := min(1, min(/.width, /.depth))
	.width *= 
	.depth *= 
	 := label.FromString(.Metadata.Original.LabelPosition)
	if  == label.Unset {
		 = label.InsideTopCenter
	}
	 := geo.NewBox(geo.NewPoint(0, 0), .Size.X/, .Size.Z/)
	 := .GetPointOnBox(, label.PADDING, .width/, .depth/)
	,  := .Position.X-.Size.X/2, .Position.Z-.Size.Z/2
	,  := +.X*+.width/2, +.Y*+.depth/2
	,  := ++.width/2, +.Size.X--.width/2
	,  := ++.depth/2, +.Size.Z--.depth/2
	 := func(, ,  float64) float64 { return max(, min(, )) }
	.center.X = (+.X*+.width/2, , )
	.center.Z = (+.Y*+.depth/2, , )
	 := make([]hierarchyHeaderObstacle, 0, 2*min(len(), d2isometric.MaxNodes))
	 := false
	var  map[string]float64
	if len() > 0 {
		 = hierarchySupportOffsets([0])
	}
	 := nativeViewDirection()
	 := func( d2isometric.Node, , , ,  float64) {
		if  < .center.Y {
			return
		}
		 = max(, .center.Y)
		,  := (-.center.Y)*.X/.Y, (-.center.Y)*.X/.Y
		,  := (-.center.Y)*.Z/.Y, (-.center.Y)*.Z/.Y
		 := hierarchyHeaderObstacle{
			x0: .Position.X -  - max(, ) - .005,
			x1: .Position.X +  - min(, ) + .005,
			z0: .Position.Z -  - max(, ) - .005,
			z1: .Position.Z +  - min(, ) + .005,
		}
		 := .x1 >=  && .x0 <= +.Size.X && .z1 >=  && .z0 <= +.Size.Z
		 := .IsOutside() && .x1 >= -.width/2 && .x0 <= +.width/2 && .z1 >= -.depth/2 && .z0 <= +.depth/2
		if  ||  {
			 = append(, )
		}
	}
	for ,  := range [:min(len(), d2isometric.MaxNodes)] {
		if .Container || .Opacity <= 0 || !hierarchyHeaderPositive(.Size.X) || !hierarchyHeaderPositive(.Size.Z) {
			continue
		}
		 =  || nativeMarkdownCard() && .BoardID == .ID
		 := hierarchyNodeRelief()
		if nativePlainMarkdownCard() {
			// The full-width sheet occupies only the upper 3.5px. Its recessed
			// support leaves visible room in a compiled container's lower label
			// margin; projecting a full-width wall to the floor hides that gap.
			,  := .Position.Y-.Size.Y/2, .Position.Y+.Size.Y/2
			 := nativeMarkdownCardInset()
			(, .Size.X/2-, .Size.Z/2-, , -markdownCardPaperDepth)
			(, .Size.X/2, .Size.Z/2, -markdownCardPaperDepth, +.002)
			continue
		}
		if nativeSolidNode() || nativeReliefSymbol() || nativeStructuredNode() || nativeMarkdownCard() {
			// hierarchyRenderNodes supplies the actual, already compressed
			// body height. Solid and relief symbols occupy the source footprint
			// with no separate base; labels sit .0015 above the body.
			if nativeStructuredNode() || nativeMarkdownCard() || !(.FillExplicit && nativePaint(.Fill, "#edf1f7").A == 0) {
				 := .Position.Y - .Size.Y/2
				(, .Size.X/2, .Size.Z/2, , +.Size.Y+.002*)
			}
			continue
		}
		// Project the solid body and low graphite base separately. Expanding
		// the full-height body by the gold pin span invents a solid wall in
		// the source label margin. Individual pins are small sparse details,
		// not a continuous text occluder. Account for the raised base floor on
		// both front and back bounds, rather than only projecting the front.
		 := .Position.Y - .Size.Y/2
		 := min(1, min(.Size.X/.6, .Size.Z/.5))
		(, .Size.X/2+.055*, .Size.Z/2+.05*, , +.09**)
		if !(.FillExplicit && nativePaint(.Fill, "#edf1f7").A == 0) {
			 := [.BoardID]
			 := max(.3*, .Size.Y+)
			,  := .Size.X/2, .Size.Z/2
			switch .Type {
			case "diamond":
				 *= 1.28
			}
			 -= 
			(, , , , ++.12*)
		}
	}
	if len() > 0 {
		 := make(map[string]*d2isometric.Node, len())
		for  := range  {
			[[].ID] = &[]
		}
		for ,  := range [0] {
			 := [.SourceID]
			if .ID == .ID ||  == nil || .Opacity <= 0 || !hierarchyPhysicalPlate(, , nativePaint(.Fill, "transparent").A) {
				continue
			}
			 := .Size.Y
			if .Kind == "platform" {
				 = .13
			}
			// Include the raised cap's centered source outline as well as its
			// wall. A parent's title must not sit behind a nested back rim.
			 := hierarchySurfaceY()
			if  <= .center.Y+.001 {
				continue
			}
			 := float64(.StrokeWidth) *  / 2
			 := *
			.Position = .Position
			(, .Size.X/2+, .Size.Z/2+, -, )
		}
	}
	 := .String()
	 := strings.Contains(, "_LEFT_") || strings.Contains(, "_RIGHT_") ||  == "INSIDE_MIDDLE_LEFT" ||  == "INSIDE_MIDDLE_RIGHT"
	if ,  := hierarchyHeaderStrip(, , , , , , );  {
		return , true
	}
	// At most eleven additional interval searches. Bottom strips are less
	// likely to be hidden because this fixed camera projects bodies upward.
	for ,  := range []float64{1, 0, .5, .25, .75, .125, .875, .375, .625} {
		 := 
		.center.Z =  + (-)*
		if ,  := hierarchyHeaderStrip(, , , , , , false);  {
			return , true
		}
	}
	for ,  := range []float64{, } {
		 := 
		.center.X = 
		if ,  := hierarchyHeaderStrip(, , , , , , true);  {
			return , true
		}
	}
	if  {
		// A source title can use the existing bottom margin right up to a
		// clear border. Try this one tighter strip before leaving the plate;
		// retain the full text allocation and keep at least 2px of edge space.
		 := max(2*, (float64(.StrokeWidth)/2+1)*)
		if  <  {
			 := 
			.center.Z =  + .Size.Z -  - .depth/2
			if ,  := hierarchyHeaderStrip(, , , , .center.Z, .center.Z, false);  {
				return , true
			}
		}
	}
	if  && .IsOutside() {
		// Tight source containers may reserve their heading outside the box.
		// If the raised document leaves no full-sized interior print strip,
		// retain that authored anchor instead of hiding the title behind it.
		 := 
		.center.X, .center.Z = , 
		if ,  := hierarchyHeaderStrip(, , .center.X, .center.X, .center.Z, .center.Z, false);  {
			return , true
		}
	}
	return , false
}

type hierarchyHeaderObstacle struct{ x0, x1, z0, z1 float64 }

func hierarchyHeaderStrip( labelSurface,  []hierarchyHeaderObstacle, , , ,  float64,  bool) (labelSurface, bool) {
	, , ,  := .center.X, .center.Z, .width, .depth
	,  := , 
	if  {
		, , ,  = .center.Z, .center.X, .depth, .width
		,  = , 
	}
	type  struct{ ,  float64 }
	 := make([], 0, len())
	for ,  := range  {
		, , ,  := .x0, .x1, .z0, .z1
		if  {
			, , ,  = , , , 
		}
		if +/2 <  || -/2 >  {
			continue
		}
		,  = -/2, +/2
		if  >=  &&  <=  {
			 = append(, {max(, ), min(, )})
		}
	}
	sort.Slice(, func(,  int) bool {
		if []. != []. {
			return []. < [].
		}
		return []. < [].
	})
	, ,  := , math.Inf(1), 
	 := func(,  float64) {
		if  >  {
			return
		}
		 := max(, min(, ))
		if  := math.Abs( - );  <  {
			,  = , 
		}
	}
	for ,  := range  {
		(, .-.001)
		 = max(, .+.001)
	}
	(, )
	if math.IsInf(, 1) {
		return , false
	}
	if  {
		.center.Z = 
	} else {
		.center.X = 
	}
	return , true
}

func hierarchyHeaderPositive( float64) bool {
	return  > 0 && !math.IsInf(, 0) && !math.IsNaN()
}