package d2isometricimg

import (
	
	
	
	
	

	
	
	
	
	
	
	
	libcolor 
)

// A printed panel retains native legend and root-frame decoration. Diagram
// components, including sequence actors and spans, use individual geometry.
type nativePanel struct {
	document      *d2scene.Document
	width, height int
	first, last   int
	animated      bool
}

func nativeSourceTarget( *d2isometric.Scene) *d2target.Diagram {
	 := d2target.NewDiagram()
	.Root, .Legend = .Root, .Legend
	.FontFamily, .MonoFontFamily = .FontFamily, .MonoFontFamily
	.Config = &d2target.Config{ThemeID: &.ThemeID, ThemeOverrides: .ThemeOverrides}
	for ,  := range .Nodes {
		.Shapes = append(.Shapes, .Metadata.Original)
	}
	for ,  := range .Edges {
		.Connections = append(.Connections, .Metadata.Original)
	}
	return 
}

func documentSurface( *d2scene.Document, ,  float64) labelSurface {
	 := .ViewBox
	return labelSurface{center: nv((.X+.Width/2)*, , (.Y+.Height/2)*), width: .Width * , depth: .Height * }
}

func sceneHasAnimation( *d2scene.Node) bool {
	if  == nil {
		return false
	}
	if len(.Animations) > 0 {
		return true
	}
	for ,  := range .Children {
		if () {
			return true
		}
	}
	return .Mask != nil && (.Mask.Root)
}

func ( *meshBuilder) ( *d2scene.Document,  labelSurface,  int) error {
	if  < 1 ||  > 2 {
		return fmt.Errorf("isometric source panel count must be at most a legend and root frame")
	}
	if !captionFinite(.ViewBox.Width, .ViewBox.Height) || .ViewBox.Width <= 0 || .ViewBox.Height <= 0 {
		return fmt.Errorf("isometric source panel has invalid dimensions")
	}
	// Unconfigured callers retain source-relative sampling. Output-aware
	// renders reserve an equal share for every planned panel, so a large first
	// panel cannot exhaust the aggregate budget before its root frame appears.
	// Dimensions only control sampling; content is never relaid out.
	 := math.Min(2, 2048/math.Max(.ViewBox.Width, .ViewBox.Height))
	,  := max(1, int(math.Ceil(.ViewBox.Width*))), max(1, int(math.Ceil(.ViewBox.Height*)))
	if .outputDensity > 0 {
		,  = surfaceTextureDimensionsAtDensity(.width, .depth, 4096, (8<<20)/, .outputDensity)
	}
	 := int64() * int64()
	for ,  := range .panels {
		 += int64(.width) * int64(.height)
	}
	if  > 8<<20 {
		return fmt.Errorf("isometric source panels exceed 8M texture pixels")
	}
	,  := rasterNativeSurfaceDocument(.ctx, , , )
	if  != nil {
		return 
	}
	 := len(.triangles)
	.surfaceTexture(, , 1)
	 := *
	.LogicalWidth, .LogicalHeight = float64(), float64()
	.ViewportFit, .ViewportAlign = d2scene.ViewportMeet, d2scene.ViewportAlignXMidYMid
	.addDocumentLinks(&, )
	for  := ;  < len(.triangles); ++ {
		.triangles[].NoDepthWrite = false
	}
	.panels = append(.panels, nativePanel{document: , width: , height: , first: , last: len(.triangles), animated: sceneHasAnimation(.Root)})
	return .err
}

func nativeMeshBounds( []Triangle) d2scene.Box {
	, , ,  := math.Inf(1), math.Inf(1), math.Inf(-1), math.Inf(-1)
	for ,  := range  {
		for ,  := range .V {
			,  = min(, .Position.X), min(, .Position.Z)
			,  = max(, .Position.X), max(, .Position.Z)
		}
	}
	if len() == 0 {
		return d2scene.Box{Width: 1, Height: 1}
	}
	return d2scene.Box{X: , Y: , Width: max(.01, -), Height: max(.01, -)}
}

func nativeBoundsTarget( *d2isometric.Scene,  d2scene.Box,  float64) *d2target.Diagram {
	 := nativeSourceTarget()
	.Shapes, .Connections, .Legend = nil, nil, nil
	 := d2target.BaseShape()
	.ID, .Type, .Fill, .Stroke, .Opacity = "native-bounds", d2target.ShapeRectangle, "transparent", "transparent", 0
	.Pos = d2target.Point{X: int(math.Floor(.X / )), Y: int(math.Floor(.Y / ))}
	.Width, .Height = max(1, int(math.Ceil(.Width/))), max(1, int(math.Ceil(.Height/)))
	.Shapes = []d2target.Shape{*}
	return 
}

func ( *meshBuilder) ( *d2isometric.Scene,  *d2scenebuild.AssetOptions) error {
	// A legend and an enclosing root border/pattern each need their own panel.
	 := 0
	if .Legend != nil {
		++
	}
	if .Root.StrokeWidth > 0 || .Root.FillPattern != "" || .Root.DoubleBorder {
		++
	}
	if .Legend != nil {
		 := nativeMeshBounds(.triangles)
		 := nativeBoundsTarget(, , .scale)
		.Legend = .Legend
		,  := nativeSurfaceDocument(.ctx, , , .fonts)
		if  != nil {
			return fmt.Errorf("isometric legend: %w", )
		}
		for ,  := range .Root.Children {
			if .ID != "legend" {
				continue
			}
			for ,  := range .Children {
				if .ID != "legend:panel" {
					continue
				}
				,  := .Primitive.(d2scene.Rect)
				if ! {
					return fmt.Errorf("isometric legend panel is not a rectangle")
				}
				.Root = 
				.ViewBox = d2scene.Box{X: .Box.X - 12, Y: .Box.Y - 12, Width: .Box.Width + 24, Height: .Box.Height + 24}
				.LogicalWidth, .LogicalHeight = .ViewBox.Width, .ViewBox.Height
				if  := .sourcePanel(, documentSurface(, .scale, .065), );  != nil {
					return 
				}
				break
			}
			break
		}
	}
	 := .Root
	if .Label != "" || .Icon != nil || .Description != "" {
		 := nativeMeshBounds(.triangles)
		 := d2themescatalog.Find(.ThemeID)
		.ApplyOverrides(.ThemeOverrides)
		.Color, .LabelFill = d2themes.ResolveThemeColor(, .Color), d2themes.ResolveThemeColor(, .LabelFill)
		if .FontSize <= 0 {
			.FontSize = 28
		}
		 := float64(.FontSize) * .scale
		 := max(.Width, float64(.LabelWidth)*.scale)
		if .Icon != nil {
			 = max(, *4)
		}
		 := max(*1.5, float64(.LabelHeight)*.scale)
		// A target built directly (without compilation) may not have metrics.
		if .LabelHeight <= 0 {
			 := 0
			for ,  := range strings.Split(.Label, "\n") {
				 += max(1, int(math.Ceil(float64(len([]rune()))**.56/)))
			}
			 = max(, float64()**1.4)
		}
		 := .Y - .35
		 := readableSurfaceInk(.Background, 1)
		if !nativeToken(.Color) {
			 = .Color
		}
		if .Description != "" {
			 := .Text
			.Label, .Language, .FontSize, .Bold = .Description, "", 16, false
			.LabelWidth, .LabelHeight = 0, 0
			 := max(.25, math.Ceil(float64(len([]rune(.Label)))*.085/)*.25)
			.label(.Label, labelSurface{center: nv(.X+/2, .066, -/2), width: , depth: , align: "left"}, , , 1, "board")
			 -=  + .18
		}
		if .Label != "" || .Icon != nil {
			 := labelSurface{center: nv(.X+/2, .066, -/2), width: , depth: , align: "left"}
			 = .shapeIcon(, , 1, "board")
			.shapeLabel(, , , 1, "board")
		}
	}
	// The ordinary background already uses Root.Fill. A framed/patterned root
	// is drawn as one source-defined ground surface enclosing all its content.
	if .StrokeWidth > 0 || .FillPattern != "" || .DoubleBorder {
		 := nativeMeshBounds(.triangles)
		.X -= .25
		.Y -= .25
		.Width += .5
		.Height += .5
		 := nativeBoundsTarget(, , .scale)
		,  := nativeSurfaceDocument(.ctx, , , .fonts)
		if  != nil {
			return fmt.Errorf("isometric root frame: %w", )
		}
		 := .Root.Children[:0]
		for ,  := range .Root.Children {
			if strings.HasPrefix(.ID, "root:") {
				 = append(, )
			}
		}
		.Root.Children = 
		if  := .sourcePanel(, documentSurface(, .scale, -.045), );  != nil {
			return 
		}
	}
	return .err
}

func finishNativeScene( *meshBuilder,  *d2isometric.Scene, ,  int,  []packetRoute) (*nativeScene, error) {
	.prepareGradientMaterials()
	if .err != nil {
		return nil, .err
	}
	 := color.NRGBA{245, 247, 251, 255}
	if !nativeToken(.Root.Fill) && !libcolor.IsGradient(.Background) {
		 := nativePaint(.Background, "#f5f7fb")
		 := float64(.A) / 255
		 = color.NRGBA{uint8(float64(.R)* + float64(.R)*(1-)), uint8(float64(.G)* + float64(.G)*(1-)), uint8(float64(.B)* + float64(.B)*(1-)), 255}
	}
	var  projectedExtent
	.animatedMesh(.triangles, .animatedNodes, .scale)
	.trafficRoutes()
	 := .camera(, , .options.fitContent)
	if .options.camera != nil {
		 = *.options.camera
	}
	,  = .width, .height
	 := &nativeScene{packets: , triangles: .triangles, panels: .panels, width: , height: , background: , animatedNodes: .animatedNodes, pixelScale: .scale, links: .links}
	if !nativeToken(.Root.Fill) && libcolor.IsGradient(.Background) {
		var  error
		.backgroundPaint,  = scenepaint.Gradient(.Background)
		if  != nil {
			return nil, 
		}
		.backgroundTexture = .gradientTexture(.Background)
		if .err != nil {
			return nil, .err
		}
	}
	.camera = 
	if .options.deferRaster {
		return , nil
	}
	 = cameraAtResolution(, *rasterAA, *rasterAA)
	,  := newRaster(nativeGradientBackgroundContext(.ctx, .backgroundTexture), , , .triangles, , &, nil)
	if  != nil {
		return nil, 
	}
	.raster = 
	return , nil
}

func ( *nativeScene) ( context.Context,  float64,  bool) (*Raster, error) {
	if ! {
		return .raster, nil
	}
	var  []Triangle
	if len(.animatedNodes) > 0 {
		 = append([]Triangle(nil), .triangles...)
		 :=  - math.Floor()
		 := 1 - math.Abs(2*-1)
		for ,  := range .animatedNodes {
			for  := .first;  < .last; ++ {
				for  := range [].V {
					[].V[].Position.Z -= 4 * .pixelScale * 
				}
				if ![].CastShadow && ![].Material.Unlit && ([].Material.Texture == nil || ![].NoDepthWrite) {
					[].CastShadow =  > 0
					[].ShadowOpacity = &
				}
			}
		}
	}
	for ,  := range .panels {
		if !.animated {
			continue
		}
		if  == nil {
			 = append([]Triangle(nil), .triangles...)
		}
		,  := rasterNativeSurfaceDocument(, .document, .width, .height, )
		if  != nil {
			return nil, 
		}
		for  := .first;  < .last; ++ {
			 := *[].Material
			.Texture, .Vector = , nativeVectorForTexture(, )
			[].Material = &
		}
	}
	if  == nil {
		return .raster, nil
	}
	// Keep the first-frame camera fixed; an animated boundary must not make
	// every other component shift or change scale from frame to frame.
	 := .raster.shadow.camera
	if .width == 0 {
		 = rasterFit(.triangles, rasterShadowDirection(), 2048, 2048, 1.08)
	}
	 := .raster.groundShadow.camera
	if .width == 0 {
		 := rasterShadowGround(.triangles)
		 = rasterFit(rasterGroundTriangles(.triangles, , nativeViewDirection()), rasterShadowDirection(), 2048, 2048, 1.08)
	}
	return newRaster(nativeGradientBackgroundContext(, .backgroundTexture), .width, .height, , .background, &.raster.camera, &, &)
}