package d2isometricimg

import (
	
	
	

	
	
	libcolor 
)

// Presentation copies can lift a nested plate within the clearance below the
// common component and route planes. Sequence groups keep their timeline's
// original background plane rather than interpreting node elevation as relief.
func hierarchySurfaceY( d2isometric.Board) float64 {
	 := hierarchyBaseSurfaceY()
	if .Kind != "sequence-group" {
		 += .Position.Y
	}
	return 
}

func hierarchyBaseSurfaceY( d2isometric.Board) float64 {
	return .028 + float64(max(0, min(128, .Level)))*.0001
}

const hierarchyTerraceCeiling = .066 // Leaves start at .07; routes remain at .08.

const hierarchyRelief = .34

// Organizational panels stay shallow, while volume-bearing source shapes
// retain enough height to read as cylinders and prisms in the same camera.
func hierarchyNodeRelief( d2isometric.Node) float64 {
	if nativeStructuredNode() || nativeMarkdownCard() {
		return 1 // Row rails and paper cards already use their selected relief.
	}
	if nativeSolidNode() {
		return .60
	}
	if nativeReliefSymbol() {
		// The selected symbol depth is already a relief, measured from its
		// source footprint. Do not compress it a second time in hierarchy.
		return 1
	}
	return hierarchyRelief
}

func hierarchyRenderNodes( []d2isometric.Node,  ...map[string]float64) []d2isometric.Node {
	 := append([]d2isometric.Node(nil), ...)
	for  := range  {
		if [].Container {
			continue
		}
		 := [].Position.Y - [].Size.Y/2
		 := [].Size.Y
		if nativeSolidNode([]) {
			// These copies are only header obstacles. Record the actual body
			// height before applying relief, including authored 3D modifiers.
			 = nativeSolidHeight([])
		} else if nativeReliefSymbol([]) || nativeStructuredNode([]) || nativeMarkdownCard([]) {
			 = nativeCanonicalHeight([], 0)
		}
		[].Size.Y =  * hierarchyNodeRelief([])
		[].Position.Y =  + [].Size.Y/2
		if len() > 0 {
			 := [0][[].BoardID]
			[].Size.Y -= 
			[].Position.Y +=  / 2
		}
	}
	return 
}

// Source layouts reserve label space in two dimensions. Low relief preserves
// those clearances in projection while retaining every footprint and texture.
func ( *meshBuilder) ( d2isometric.Node,  string) {
	 := len(.triangles)
	 := len(.links)
	 := hierarchyNodeRelief()
	 := .nodeSupportDrop
	.nodeSupportDrop = .hierarchySupports[.BoardID] / 
	.node(, , )
	.nodeSupportDrop = 
	 := .Position.Y - .Size.Y/2
	for  := ;  < len(.triangles); ++ {
		for  := range .triangles[].V {
			 := &.triangles[].V[]
			.Position.Y =  + float64((.Position.Y-)*)
			.Normal = nunit(nv(.Normal.X, .Normal.Y/, .Normal.Z))
		}
	}
	 := len(.triangles)
	if !nativeStructuredNode() && !nativeMarkdownCard() {
		.classicInkEdges(, .triangles[:])
	}
	if .Metadata.Original.Animated && len(.animatedNodes) > 0 {
		 := &.animatedNodes[len(.animatedNodes)-1]
		if .first ==  && .last ==  {
			.last = len(.triangles)
		}
	}
	for  := ;  < len(.links); ++ {
		for  := range .links[].points {
			 := &.links[].points[]
			.Y =  + float64((.Y-)*)
		}
	}
	.markPaintOwner()
}

func hierarchySpacer( d2isometric.Node) bool {
	return strings.TrimSpace(.Label) == "" && .Icon == "" &&
		nativePaint(.Fill, "#edf1f7").A == 0 &&
		(.StrokeWidth == 0 || nativePaint(.Stroke, "#849ebc").A == 0)
}

// Blank white/transparent grid wrappers carry layout, not a physical boundary.
// Their source nodes and links remain in the scene; only decorative paint is
// omitted. Named or visibly colored regions are never removed by this rule.
func hierarchyLayoutWrapper( *d2isometric.Node) bool {
	if  == nil || strings.TrimSpace(.Label) != "" || .Icon != "" {
		return false
	}
	,  := nativePaint(.Fill, "white"), nativePaint(.Stroke, "#849ebc")
	 := .A == 0 || .R >= 245 && .G >= 245 && .B >= 245
	 := .StrokeWidth == 0 || .A == 0 ||  == 
	return  && 
}

func hierarchyPresentationBoards( []d2isometric.Board,  map[string]*d2isometric.Node) []d2isometric.Board {
	 := append([]d2isometric.Board(nil), ...)
	 := make(map[string]d2isometric.Board, len())
	for ,  := range  {
		[.ID] = 
	}
	for  := range  {
		 := &[]
		if hierarchyLayoutWrapper([.SourceID]) {
			.Kind, .Label = "ungrouped", ""
			continue
		}
		if .Kind != "group" {
			continue
		}
		 := .ParentID
		for  := 0;  != "" &&  < len(); ++ {
			,  := []
			if ! || !hierarchyLayoutWrapper([.SourceID]) {
				break
			}
			 = .ParentID
		}
		if  == "" {
			.Kind = "platform"
		}
	}
	hierarchyTerraces(, )
	return 
}

// Thickness and elevation belong to presentation copies only. An invisible
// wrapper, dashed scope or transparent region cannot add another physical tier.
func hierarchyTerraces( []d2isometric.Board,  map[string]*d2isometric.Node) {
	 := make(map[string]int, len())
	for  := range  {
		[[].ID] = 
		[].Position.Y = 0
	}
	// Sequence annotations have independent source scopes and timeline planes.
	// Keep their ancestor backgrounds at the original elevation too, avoiding
	// a new opaque plate hiding a sequence's independently emitted group wash.
	 := make(map[string]bool)
	for ,  := range  {
		if  := [.SourceID];  != nil && .SequenceRole == "container" {
			for ,  := .ID, 0;  != "" &&  < len(); ++ {
				if [] {
					break
				}
				[] = true
				,  := []
				if ! {
					break
				}
				 = [].ParentID
			}
		}
	}
	 := make([]uint8, len())
	 := make([]bool, len())
	var  func(int)
	 = func( int) {
		if [] != 0 {
			return
		}
		[] = 1
		 := &[]
		 := hierarchyBaseSurfaceY(*)
		 := .028
		if ,  := [.ParentID];  && [] != 1 {
			()
			 = hierarchySurfaceY([])
			[] = []
		}
		 := [.SourceID]
		if  != nil && .SequenceRole == "container" {
			[] = true
		}
		if .Kind == "ungrouped" {
			// Carry the nearest visible cap through layout-only wrappers.
			.Position.Y = max(0, -)
		}
		if .Kind == "group" {
			.Size.Y = 0
			if ![.ID] && ![] {
				 := max(, )
				if  != nil && .SequenceRole == "" && .Opacity > 0 && .StrokeDash == 0 && nativePaint(.Fill, "transparent").A > 0 {
					// Spend most clearance on the first tier, then progressively
					// smaller steps. Even deep source nesting stays below leaves.
					 += max(0, hierarchyTerraceCeiling-) * (.030 / .038)
					.Size.Y = max(0, -)
				}
				.Position.Y = max(0, -)
			}
		}
		[] = 2
	}
	for  := range  {
		()
	}
	hierarchyStrongTerraces(, , , )
}

func hierarchyPhysicalPlate( d2isometric.Board,  *d2isometric.Node,  uint8) bool {
	if  == nil ||  == 0 || .StrokeDash > 0 {
		return false
	}
	return .Kind == "platform" || .Kind == "terrace" || .Kind == "group" && .Position.Y > 0 && .SequenceRole == ""
}

func hierarchyCasingFloor( []d2isometric.Board,  map[string]*d2isometric.Node) float64 {
	 := 0.
	for ,  := range  {
		 := [.SourceID]
		if (.Kind == "terrace" || .Kind == "group" && .Position.Y > 0) &&  != nil && .Opacity > 0 && hierarchyPhysicalPlate(, , nativePaint(.Fill, "transparent").A) {
			 = max(, hierarchySurfaceY()+.0005)
		}
	}
	return 
}

// Header contrast follows the actually visible parent surface, including the
// translucent washes between it and this label, rather than the raw fill hue.
func hierarchyHeaderInk( d2isometric.Board,  map[string]d2isometric.Board,  map[string]*d2isometric.Node,  map[string]string,  string) string {
	 := []d2isometric.Board{}
	for  := .ParentID;  != "" && len() <= len(); {
		,  := []
		if ! {
			break
		}
		 = append(, )
		 = .ParentID
	}
	, ,  := 245., 247., 251.
	 := nativePaint(, "#f5f7fb")
	 := float64(.A) / 255
	, ,  = float64(.R)*+*(1-), float64(.G)*+*(1-), float64(.B)*+*(1-)
	for  := len() - 1;  >= 0; -- {
		 := []
		 := [.SourceID]
		if  == nil || .Kind == "ungrouped" {
			continue
		}
		 := nativePaint([.ID], "#edf1f7")
		 := float64(.A) / 255 * .Opacity
		if !hierarchyPhysicalPlate(, , .A) {
			 *= .22
		}
		, ,  = float64(.R)*+*(1-), float64(.G)*+*(1-), float64(.B)*+*(1-)
	}
	return readableSurfaceInk(fmt.Sprintf("#%02x%02x%02x", uint8(), uint8(), uint8()), 1)
}

func hierarchyBoardTint( *d2isometric.Node,  string) string {
	// Source containers already have their theme colors resolved. Their large
	// background surfaces must retain that paint, including transparent fills;
	// a level accent can otherwise turn a pale source container into a dark slab.
	if  != nil && .Fill != "" {
		return .Fill
	}
	return 
}

// A source container encloses its actual descendants. Organizational regions
// have a fine printed boundary and wash, while outer systems retain a platform.
func ( *meshBuilder) ( d2isometric.Board,  *d2isometric.Node,  string,  float64) {
	if .Kind == "ungrouped" ||  == nil ||  <= 0 || .Size.X <= 0 || .Size.Z <= 0 {
		return
	}
	 := len(.triangles)
	 := nativePaint(, "#edf1f7")
	 := hierarchyPhysicalPlate(, , .A)
	if ! && .Kind != "sequence-group" {
		.A = uint8(math.Round(float64(.A) * .22))
	}
	 := *
	.Size = .Size
	 := richColor()
	if libcolor.IsGradient() {
		// Authored gradients are source paint, not a representative solid swatch.
		 = 
		if ! && .Kind != "sequence-group" {
			var  error
			,  = nativeGradientOpacity(, .22)
			if  != nil {
				.err = 
				return
			}
		}
	}
	 := nativeFaceSource(, )
	,  := nativeShapeProfiles()
	if  != nil {
		.err = 
		return
	}
	,  := .nativeFace()
	if .err != nil {
		return
	}
	,  := .Size.X/float64(.Width), .Size.Z/float64(.Height)
	 := .scale
	if  <= 0 {
		 = .01
	}
	 := hierarchySurfaceY()
	 := 0.
	if  {
		 = .13
		if .Kind == "group" || .Kind == "terrace" {
			 = .Size.Y
		}
	}
	if .Metadata.Original.ThreeDee {
		 += d2target.THREE_DEE_OFFSET * 
	}
	 := nativeMaterial(, .62, .12, )
	.Color = tintDark(.Color, .85)
	for  := 1;  >= 0; -- {
		if  == 1 && !.Metadata.Original.Multiple {
			continue
		}
		,  := float64()*d2target.MULTIPLE_OFFSET*, -float64()*d2target.MULTIPLE_OFFSET*
		 :=  - float64()*.00001
		if  > 0 {
			for ,  := range  {
				 := make([]Vec, len())
				for ,  := range  {
					[] = nv(.Position.X-.Size.X/2+float64(.X*)+, , .Position.Z-.Size.Z/2+float64(.Z*)+)
				}
				.extrudedProfile(, -, nil, )
			}
		}
		 := 
		.center = nv(.Position.X-.Size.X/2+float64(.center.X*)+, , .Position.Z-.Size.Z/2+float64(.center.Z*)+)
		.width, .depth = .width*, .depth*
		 := len(.triangles)
		.surfaceTexture(, , )
		for  := ;  < len(.triangles); ++ {
			.triangles[].NoDepthWrite = false
			.triangles[].Material.Unlit = !
			.triangles[].CastShadow =  || .Metadata.Original.Shadow
			if .Kind == "sequence-group" {
				.triangles[].Material.Multiply = true
				.triangles[].NoDepthWrite = true
			}
		}
	}
	nativePhysicalShadows(.triangles[:], .Metadata.Original.Shadow)
	if .Metadata.Original.Animated && len(.triangles) >  {
		.animatedNodes = append(.animatedNodes, nativeAnimatedNode{first: , last: len(.triangles)})
	}
}

func ( *meshBuilder) ( Vec, , ,  float64,  *Material) {
	,  := nv(.X-/2, , .Z-/2), nv(.X+/2, , .Z-/2)
	,  := nv(.X+/2, , .Z+/2), nv(.X-/2, , .Z+/2)
	.flat(, , , , false)
	.flat(, , , , false)
}