package d2isometricimg

import (
	
	
	
	
	
	
	
	

	
	
	
	
	
	
)

const (
	maxRichLabelBytes  = 64 * 1024
	maxRichSourceBytes = 1024 * 1024
	maxRichRows        = 512
	maxRichTotalRows   = 4096
	maxRichPixels      = 8 * 1024 * 1024
)

// Rich labels reuse D2's typed native Markdown, Chroma and structured-table
// layout. Only their paint is rasterized; no compiler layout or source shape
// dimensions are changed and no browser or external asset resolver is used.
type richLabelPainter struct {
	themeID                                *int64
	themeOverrides                         *d2target.ThemeOverrides
	fallbackFonts                          *d2scenebuild.FontFallbackOptions
	ctx                                    context.Context
	count, used, pixels, sourceBytes, rows int
	primary, mono                          d2fonts.FontFamily
	outputDensity                          float64
	lastDocument                           *d2scene.Document
}

func newRichLabelPainter( context.Context,  int) (*richLabelPainter, error) {
	if  == nil {
		return nil, fmt.Errorf("isometric rich labels require a context")
	}
	if  := .Err();  != nil {
		return nil, 
	}
	if  < 0 ||  > maxTextLabels {
		return nil, fmt.Errorf("isometric rich labels exceed %d labels", maxTextLabels)
	}
	return &richLabelPainter{ctx: , count: }, nil
}

func ( *richLabelPainter) (,  *d2fonts.FontFamily) {
	.primary, .mono = nativeFontFamilies(, )
}

func ( *richLabelPainter) ( *d2scenebuild.FontFallbackOptions) {
	.fallbackFonts = 
}

func ( *richLabelPainter) ( float64) {
	.outputDensity = 
}

func ( *richLabelPainter) ( int64,  *d2target.ThemeOverrides) {
	.themeID, .themeOverrides = &, 
}

func isRichLabel( d2target.Shape) bool {
	 := strings.ToLower(strings.TrimSpace(.Language))
	return .Type == d2target.ShapeSQLTable || .Type == d2target.ShapeClass ||
		.Type == d2target.ShapeCode ||  != ""
}

func ( *richLabelPainter) ( d2target.Shape,  labelTextStyle) (*image.RGBA, error) {
	.lastDocument = nil
	if  := .ctx.Err();  != nil {
		return nil, 
	}
	if .used >= .count {
		return nil, fmt.Errorf("isometric rich label allocation exceeds its declared count")
	}
	if !isRichLabel() {
		return nil, fmt.Errorf("isometric rich label has no supported rich content")
	}
	if !captionFinite(.Width, .Depth, .PixelScale, .FontSize, .Opacity) ||
		.Width <= 0 || .Depth <= 0 || .PixelScale <= 0 || .FontSize < 0 || .Opacity < 0 || .Opacity > 1 {
		return nil, fmt.Errorf("isometric rich label has invalid surface dimensions or style")
	}
	, ,  := richSourceSize()
	if  != nil {
		return nil, 
	}
	if  > maxRichSourceBytes-.sourceBytes ||  > maxRichTotalRows-.rows {
		return nil, fmt.Errorf("isometric rich labels exceed aggregate source or row budget")
	}
	.sourceBytes += 
	.rows += 
	.used++
	,  := richLabelDocumentWithResources(.ctx, , , .fallbackFonts, .themeID, .themeOverrides, .primary, .mono)
	if  != nil {
		return nil, fmt.Errorf("isometric rich label %q: %w", .ID, )
	}
	 := .Type == d2target.ShapeSQLTable || .Type == d2target.ShapeClass
	if ! {
		if  := fitRichViewport(, );  != nil {
			return nil, 
		}
	}
	// Budget each tile by its actual physical aspect ratio, not a fixed wide
	// text atlas patch. Tall Markdown and SQL rows therefore retain detail.
	 := min(4*1024*1024, maxRichPixels/max(1, .count))
	,  := surfaceTextureDimensionsAtDensity(.Width, .Depth, 4096, , .outputDensity)
	if * > maxRichPixels-.pixels {
		return nil, fmt.Errorf("isometric rich labels exceed texture pixel budget")
	}
	.pixels +=  * 
	.LogicalWidth, .LogicalHeight = float64(), float64()
	.ViewportFit = d2scene.ViewportMeet
	if  {
		.ViewportFit = d2scene.ViewportStretch
	}
	.ViewportAlign = d2scene.ViewportAlignXMidYMid
	,  := d2raster.Render(.ctx, , richRasterOptions())
	if  != nil {
		return nil, 
	}
	 := image.NewRGBA(.Bounds())
	,  := nativeLabelBackground(.ctx, , , )
	if  != nil {
		return nil, 
	}
	if  != nil {
		draw.Draw(, .Bounds(), , image.Point{}, draw.Src)
	}
	draw.Draw(, .Bounds(), , image.Point{}, draw.Over)
	// Text and any inline backgrounds receive object opacity once. Conversion
	// through draw.Draw keeps bilinear sampling in premultiplied RGBA space.
	if .Opacity < 1 {
		for  := 0;  < .Rect.Dy(); ++ {
			if &31 == 0 {
				if  := .ctx.Err();  != nil {
					return nil, 
				}
			}
			for  := 0;  < .Rect.Dx()*4; ++ {
				 := *.Stride + 
				.Pix[] = uint8(math.Round(float64(.Pix[]) * .Opacity))
			}
		}
	}
	if  := .ctx.Err();  != nil {
		return nil, 
	}
	.lastDocument = 
	if  := retainStyledNativeVectorSurface(.ctx, , , nativeLabelBackgroundPaint(), .Opacity);  != nil {
		return nil, 
	}
	return , nil
}

func richSourceSize( d2target.Shape) (int, int, error) {
	 := len(.Fields) + len(.Methods) + len(.Columns)
	if  > maxRichRows {
		return 0, 0, fmt.Errorf("isometric rich label exceeds %d structured rows", maxRichRows)
	}
	 := 0
	 := func( string) error {
		if len() > maxRichLabelBytes- || !utf8.ValidString() {
			return fmt.Errorf("isometric rich label exceeds source byte budget or contains invalid UTF-8")
		}
		 += len()
		return nil
	}
	if  := (.Label);  != nil {
		return 0, 0, 
	}
	for ,  := range .Fields {
		for ,  := range []string{.Name, .Type, .Visibility} {
			if  := ();  != nil {
				return 0, 0, 
			}
		}
	}
	for ,  := range .Methods {
		for ,  := range []string{.Name, .Return, .Visibility} {
			if  := ();  != nil {
				return 0, 0, 
			}
		}
	}
	for ,  := range .Columns {
		if len(.Constraint) > maxRichRows {
			return 0, 0, fmt.Errorf("isometric rich label exceeds constraint limit")
		}
		if  := (.Name.Label);  != nil {
			return 0, 0, 
		}
		if  := (.Type.Label);  != nil {
			return 0, 0, 
		}
		for ,  := range .Constraint {
			if  := ();  != nil {
				return 0, 0, 
			}
		}
	}
	return , , nil
}

func richLabelDocument( context.Context,  d2target.Shape,  labelTextStyle,  ...d2fonts.FontFamily) (*d2scene.Document, error) {
	return richLabelDocumentWithFonts(, , , nil, ...)
}

func richLabelDocumentWithFonts( context.Context,  d2target.Shape,  labelTextStyle,  *d2scenebuild.FontFallbackOptions,  ...d2fonts.FontFamily) (*d2scene.Document, error) {
	return richLabelDocumentWithResources(, , , , nil, nil, ...)
}

func richLabelDocumentWithResources( context.Context,  d2target.Shape,  labelTextStyle,  *d2scenebuild.FontFallbackOptions,  *int64,  *d2target.ThemeOverrides,  ...d2fonts.FontFamily) (*d2scene.Document, error) {
	// Construct a minimal owned target. Icons, links, shadows and other object
	// effects belong to the 3D object or its own asset phase, never this texture.
	 := d2target.Shape{ID: "surface", Type: .Type, Opacity: 1, Stroke: "none", Fill: "transparent", Text: .Text,
		Class: .Class, SQLTable: .SQLTable, LabelPosition: "INSIDE_MIDDLE_CENTER"}
	.Language = strings.ToLower(strings.TrimSpace(.Language))
	if .Language == "md" {
		.Language = "markdown"
	}
	if .FontSize <= 0 {
		.FontSize = max(1, int(math.Round(.FontSize)))
	}
	if .FontSize <= 0 {
		.FontSize = 16
	}
	if .FontSize > 10000 {
		return nil, fmt.Errorf("rich font size exceeds limit")
	}
	,  := nativeFontFamilies(nil, nil)
	if len() > 0 && [0] != "" {
		 = [0]
	}
	if len() > 1 && [1] != "" {
		 = [1]
	}
	,  := nativeFontFamily(.FontFamily, , )
	if  != nil {
		return nil, 
	}
	 = 
	.FontFamily = "DEFAULT"
	 := richColor(.Color)
	if .ForegroundPaint != nil {
		 = .Foreground
	}
	.Color = 
	if .Language == "latex" {
		.Stroke = 
	}
	 := .Type == d2target.ShapeSQLTable || .Type == d2target.ShapeClass
	if  {
		.Width, .Height = .Width, .Height
		// Structured D2 shapes use Fill for the header and row ink, and
		// Stroke for their body background. Keep this native contract and
		// theme tokens; never recolor every row with a generic caption ink.
		.Fill, .Stroke, .Color = .Fill, .Stroke, .Color
		if .Fill == "" {
			.Fill = 
		}
		if .Stroke == "" {
			.Stroke = "transparent"
		}
		if .Color == "" {
			.Color = readableSurfaceInk(.Fill, 1)
		}
		.BorderRadius = .BorderRadius
		.PrimaryAccentColor = .PrimaryAccentColor
		.SecondaryAccentColor = .SecondaryAccentColor
		.NeutralAccentColor = .NeutralAccentColor
		if .PrimaryAccentColor == "" {
			.PrimaryAccentColor = 
		}
		if .SecondaryAccentColor == "" {
			.SecondaryAccentColor = 
		}
		if .NeutralAccentColor == "" {
			.NeutralAccentColor = 
		}
	} else {
		.LabelWidth = .LabelWidth
		.LabelHeight = .LabelHeight
		if .LabelWidth <= 0 {
			.LabelWidth = max(1, int(math.Min(100000, .Width/.PixelScale)))
		}
		if .LabelHeight <= 0 {
			.LabelHeight = max(1, int(math.Min(100000, .Depth/.PixelScale)))
		}
		.Width, .Height = .LabelWidth, .LabelHeight
		if .Language == "markdown" || .Language == "latex" {
			.Type = d2target.ShapeText
		} else {
			.Type = d2target.ShapeCode
			if .Language == "" {
				.Language = "text"
			}
			.Width += .FontSize
			.Height += .FontSize
		}
	}
	if .Width <= 0 || .Height <= 0 || .Width > 100000 || .Height > 100000 || .LabelWidth < 0 || .LabelHeight < 0 || .LabelWidth > 100000 || .LabelHeight > 100000 {
		return nil, fmt.Errorf("rich label dimensions exceed limits")
	}
	 := d2target.NewDiagram()
	.Root.Fill, .Root.Stroke = "transparent", "none"
	.FontFamily, .MonoFontFamily = &, &
	.Shapes = []d2target.Shape{}
	 := int64(0)
	 := d2themescatalog.NeutralDefault.ID
	// Root chooses light default ink for a dark physical face; use matching
	// Markdown inline palettes and code syntax colors on that face.
	if int(.Color.R)*299+int(.Color.G)*587+int(.Color.B)*114 > 180000 {
		 = d2themescatalog.DarkMauve.ID
	}
	,  := nativeSurfaceAssets(, nil)
	if  != nil {
		return nil, 
	}
	if  != nil {
		 = *
	}
	,  := nativeSurfaceFonts()
	if  != nil {
		return nil, 
	}
	,  := d2scenebuild.Build(, , d2scenebuild.Options{Assets: , Pad: &, ThemeID: &, ThemeOverrides: , MaxNodes: 20000, MaxPathCommands: 250000,
		LinkBudget: d2scenebuild.LinkBudget{MaxRegions: 2048, MaxStringBytes: maxRichLabelBytes}, Fonts: })
	if  != nil {
		return nil, 
	}
	if  := nativeDocumentGlyphCoverage(, );  != nil {
		return nil, 
	}
	.ViewBox = d2scene.Box{Width: float64(.Width), Height: float64(.Height)}
	if  {
		stripRichBodies(.Root)
	}
	// Retain inline link geometry for exports that support annotations. Links
	// remain metadata: generating the surface never resolves or fetches them.
	return , nil
}

// Preserve the original D2 font size when a component has spare physical
// space. Smaller print surfaces uniformly shrink the rich area to fit.
func fitRichViewport( *d2scene.Document,  labelTextStyle) error {
	,  := .Width/.PixelScale, .Depth/.PixelScale
	if !captionFinite(, ) ||  <= 0 ||  <= 0 {
		return fmt.Errorf("isometric rich label has invalid physical pixel dimensions")
	}
	 := .ViewBox
	 := math.Min(1, math.Min(/.Width, /.Height))
	,  = /, /
	.ViewBox = d2scene.Box{X: .X + (.Width-)/2, Y: .Y + (.Height-)/2, Width: , Height: }
	return nil
}

func richColor( color.NRGBA) string { return fmt.Sprintf("#%02x%02x%02x%02x", .R, .G, .B, .A) }
func richAccent(,  string) string {
	if  == "" || nativeToken() {
		return 
	}
	return 
}
func stripRichBodies( *d2scene.Node) {
	if  == nil {
		return
	}
	if .ID == "surface:outline" {
		.Primitive = nil
	}
	for ,  := range .Children {
		()
	}
}

func richRasterOptions() d2raster.FrameOptions {
	return d2raster.FrameOptions{Scale: 1, MaxWidth: 4096, MaxHeight: 4096, MaxPixels: 8 * 1024 * 1024,
		MaxNodes: 100000, MaxDepth: 128, MaxPathCommands: 500000, MaxTextRunesPerRun: maxRichLabelBytes,
		MaxTextCoverageChecks: 2_000_000, MaxTextShapingRuns: 20000, MaxFontFacesPerText: 4,
		MaxAnimationTracks: 1, MaxAnimationKeyframes: 1, MaxAssets: 4096, MaxAssetBytes: 32 * 1024 * 1024,
		MaxDecodedAssetBytes: 1, MaxImportDepth: 8, MaxOffscreenBytes: 32 * 1024 * 1024,
		MaxEvenOddClipWork: 20_000_000, MaxScanlineWork: 100_000_000}
}