package d2scenebuild

import (
	
	

	
	
	
	
)

func ( *builder) ( d2target.Shape,  d2scene.Paint,  *d2scene.Stroke) ([]*d2scene.Node, error) {
	if  := validateStructuredShape();  != nil {
		return nil, 
	}
	 := d2scene.Box{
		X: float64(.Pos.X), Y: float64(.Pos.Y),
		Width: float64(.Width), Height: float64(.Height),
	}
	 := clampBorderRadius(float64(.BorderRadius), .Width, .Height)
	 := d2scene.NewNode(d2scene.Rect{Box: , RadiusX: , RadiusY: , Fill: , Stroke: })
	.ID = .ID + ":outline"
	 := []*d2scene.Node{}

	switch .Type {
	case d2target.ShapeClass:
		,  := .buildClassContents(, , )
		if  != nil {
			return nil, 
		}
		return append(, ...), nil
	case d2target.ShapeSQLTable:
		,  := .buildSQLTableContents(, , )
		if  != nil {
			return nil, 
		}
		return append(, ...), nil
	default:
		return nil, unsupported(fmt.Sprintf("shape %q", .ID), "structured shape "+.Type)
	}
}

func validateStructuredShape( d2target.Shape) error {
	 := fmt.Sprintf("shape %q", .ID)
	if .FontSize <= 0 && (.Label != "" || len(.Fields) != 0 || len(.Methods) != 0 || len(.Columns) != 0) {
		return invalidField(, "fontSize", .FontSize, "must be positive for a structured shape with text")
	}
	if .Width <= 0 || .Height <= 0 {
		return invalidField(, "dimensions", fmt.Sprintf("%dx%d", .Width, .Height), "must be positive for a structured shape")
	}
	switch .Type {
	case d2target.ShapeClass:
		 := 2 + len(.Fields) + len(.Methods)
		 := float64(.Height) / float64()
		 := math.Max(2*, float64(.LabelHeight)+2*label.PADDING)
		if  > float64(.Height) {
			return invalidField(, "labelHeight", .LabelHeight, "does not fit the class height")
		}
	case d2target.ShapeSQLTable:
		// Width, height, and text constraints above are the complete static
		// table layout preconditions.
	default:
		return unsupported(, "structured shape "+.Type)
	}
	return nil
}

func ( *builder) ( d2target.Shape,  d2scene.Box,  float64) ([]*d2scene.Node, error) {
	 := 2 + len(.Fields) + len(.Methods)
	 := .Height / float64()
	 := math.Max(2*, float64(.LabelHeight)+2*label.PADDING)
	 := d2scene.Box{X: .X, Y: .Y, Width: .Width, Height: }
	,  := .paint(.Fill, fmt.Sprintf("shape %q class header", .ID))
	if  != nil {
		return nil, 
	}
	 := d2scene.NewNode(structuredHeaderPrimitive(, , , ))
	.ID = .ID + ":class-header"
	 := []*d2scene.Node{}

	if .Label != "" {
		 := .Text
		.FontFamily = "mono"
		.FontSize = .FontSize + d2target.HeaderFontAdd
		// Structured-shape text uses the SVG renderer's dedicated regular
		// class faces rather than the bold default carried by BaseShape.
		.Bold = false
		.Italic = false
		,  := .font()
		if  != nil {
			return nil, fmt.Errorf("scene: shape %q class header: %w", .ID, )
		}
		,  := .paint(.GetFontColor(), fmt.Sprintf("shape %q class header text", .ID))
		if  != nil {
			return nil, 
		}
		 := sceneBoxToGeo()
		 := label.InsideMiddleCenter.GetPointOnBox(, 0, float64(.LabelWidth), float64(.LabelHeight))
		 := .ID + ":class-header-label"
		 := centeredTextRuns(
			, .Label, ,
			.LabelWidth, .LabelHeight, .FontSize,
			, , false,
		)
		// Preserve the historical single-line scene ID while assigning stable,
		// unique suffixes to additional multiline baselines.
		for ,  := range  {
			if  == 0 {
				.ID = 
			} else {
				.ID = fmt.Sprintf("%s:%d", , )
			}
		}
		 = append(, ...)
	}

	,  := .paint(.PrimaryAccentColor, fmt.Sprintf("shape %q class primary accent", .ID))
	if  != nil {
		return nil, 
	}
	,  := .paint(.Fill, fmt.Sprintf("shape %q class row name", .ID))
	if  != nil {
		return nil, 
	}
	,  := .paint(.SecondaryAccentColor, fmt.Sprintf("shape %q class secondary accent", .ID))
	if  != nil {
		return nil, 
	}
	 := .Text
	.FontFamily = "mono"
	.Bold = false
	.Italic = false
	,  := .font()
	if  != nil {
		return nil, fmt.Errorf("scene: shape %q class row: %w", .ID, )
	}
	 := d2scene.Box{X: .X, Y: .Y + , Width: .Width, Height: }
	for ,  := range .Fields {
		 = append(, buildClassRowNodes(.ID, "field", , , float64(.FontSize), ,
			.VisibilityToken(), .Name, .Type, .Underline, , , )...)
		.Y += 
	}

	,  := .X, .X+.Width
	if  != 0 && len(.Methods) == 0 {
		 += 
		 -= 
	}
	,  := .paint(.Fill, fmt.Sprintf("shape %q class separator", .ID))
	if  != nil {
		return nil, 
	}
	 := d2scene.NewNode(d2scene.Path{
		Commands: []d2scene.PathCommand{d2scene.MoveTo(, .Y), d2scene.LineTo(, .Y)},
		Stroke:   &d2scene.Stroke{Paint: , Width: 1, Cap: d2scene.CapButt, Join: d2scene.JoinMiter, MiterLimit: 4},
	})
	.ID = .ID + ":class-separator"
	 = append(, )

	for ,  := range .Methods {
		 = append(, buildClassRowNodes(.ID, "method", , , float64(.FontSize), ,
			.VisibilityToken(), .Name, .Return, .Underline, , , )...)
		.Y += 
	}
	return , nil
}

func buildClassRowNodes(,  string,  int,  d2scene.Box,  float64,  d2scene.Font, , ,  string,  bool, , ,  d2scene.Paint) []*d2scene.Node {
	 := sceneBoxToGeo()
	 := label.InsideMiddleLeft.GetPointOnBox(, d2target.PrefixPadding, .Width, )
	 := label.InsideMiddleRight.GetPointOnBox(, d2target.TypePadding, 0, )
	 := .Y + *3/4
	 := fmt.Sprintf("%s:class-%s:%d:prefix", , , )
	 := fmt.Sprintf("%s:class-%s:%d:name", , , )
	 := fmt.Sprintf("%s:class-%s:%d:type", , , )
	return []*d2scene.Node{
		structuredTextNode(, , d2scene.Point{X: .X, Y: }, d2scene.AnchorStart, , , false, ),
		structuredTextNode(, , d2scene.Point{X: .X + d2target.PrefixWidth, Y: }, d2scene.AnchorStart, , , , ),
		structuredTextNode(, , d2scene.Point{X: .X, Y: }, d2scene.AnchorEnd, , , false, ),
	}
}

func ( *builder) ( d2target.Shape,  d2scene.Box,  float64) ([]*d2scene.Node, error) {
	 := .Height / float64(1+len(.Columns))
	 := d2scene.Box{X: .X, Y: .Y, Width: .Width, Height: }
	,  := .paint(.Fill, fmt.Sprintf("shape %q table header", .ID))
	if  != nil {
		return nil, 
	}
	 := d2scene.NewNode(structuredHeaderPrimitive(, , , ))
	.ID = .ID + ":table-header"
	 := []*d2scene.Node{}

	if .Label != "" {
		 := .Text
		.FontSize = .FontSize + d2target.HeaderFontAdd
		.Bold = false
		.Italic = false
		,  := .font()
		if  != nil {
			return nil, fmt.Errorf("scene: shape %q table header: %w", .ID, )
		}
		,  := .paint(.GetFontColor(), fmt.Sprintf("shape %q table header text", .ID))
		if  != nil {
			return nil, 
		}
		 := sceneBoxToGeo()
		 := label.InsideMiddleLeft.GetPointOnBox(, float64(d2target.HeaderPadding), .Width, float64(.LabelHeight))
		 = append(, structuredTextNode(
			.ID+":table-header-label", .Label,
			d2scene.Point{X: .X, Y: .Y + float64(.LabelHeight)*3/4},
			d2scene.AnchorStart, , , false, ,
		))
	}

	,  := 0, 0
	for ,  := range .Columns {
		if .Name.LabelWidth >  {
			 = .Name.LabelWidth
		}
		if .Type.LabelWidth >  {
			 = .Type.LabelWidth
		}
	}
	_ =  // retained to mirror the measured table layout contract.
	 := .Text
	.Bold = false
	.Italic = false
	,  := .font()
	if  != nil {
		return nil, fmt.Errorf("scene: shape %q table row: %w", .ID, )
	}
	,  := .paint(.PrimaryAccentColor, fmt.Sprintf("shape %q table name accent", .ID))
	if  != nil {
		return nil, 
	}
	,  := .paint(.NeutralAccentColor, fmt.Sprintf("shape %q table type accent", .ID))
	if  != nil {
		return nil, 
	}
	,  := .paint(.SecondaryAccentColor, fmt.Sprintf("shape %q table constraint accent", .ID))
	if  != nil {
		return nil, 
	}
	,  := .paint(.Fill, fmt.Sprintf("shape %q table separator", .ID))
	if  != nil {
		return nil, 
	}
	 := d2scene.Box{X: .X, Y: .Y + , Width: .Width, Height: }
	for ,  := range .Columns {
		 := sceneBoxToGeo()
		 := label.InsideMiddleLeft.GetPointOnBox(, d2target.NamePadding, 0, float64(.FontSize))
		 := .Y + float64(.FontSize)*3/4
		 := fmt.Sprintf("%s:table-row:%d", .ID, )
		 = append(,
			structuredTextNode(+":name", .Name.Label, d2scene.Point{X: .X, Y: }, d2scene.AnchorStart, , , false, ),
			structuredTextNode(+":type", .Type.Label, d2scene.Point{X: .X + float64(+d2target.TypePadding), Y: }, d2scene.AnchorStart, , , false, ),
			structuredTextNode(+":constraint", .ConstraintAbbr(), d2scene.Point{X: .X + .Width - d2target.NamePadding, Y: }, d2scene.AnchorEnd, , , false, ),
		)
		.Y += 
		,  := .X, .X+.Width
		if  == len(.Columns)-1 &&  != 0 {
			 += 
			 -= 
		}
		 := d2scene.NewNode(d2scene.Path{
			Commands: []d2scene.PathCommand{d2scene.MoveTo(, .Y), d2scene.LineTo(, .Y)},
			Stroke:   &d2scene.Stroke{Paint: , Width: 2, Cap: d2scene.CapButt, Join: d2scene.JoinMiter, MiterLimit: 4},
		})
		.ID =  + ":separator"
		 = append(, )
	}
	return , nil
}

func structuredHeaderPrimitive(,  d2scene.Box,  float64,  d2scene.Paint) d2scene.Primitive {
	if  == 0 {
		return d2scene.Rect{Box: , Fill: }
	}
	 = math.Min(, math.Min(.Width/2, .Height))
	, , ,  := .X, .Y, .Width, .Height
	 := []d2scene.PathCommand{
		d2scene.MoveTo(, +),
		// Smooth cubics after a line use the current point as the reflected first
		// control.
		d2scene.CubicTo(, +, , , +, ),
		d2scene.LineTo(+-, ),
		d2scene.CubicTo(+-, , +, , +, +),
	}
	if .Height >= .Height {
		 = append(,
			d2scene.LineTo(+, +-),
			d2scene.CubicTo(+, +-, +, +, +-, +),
			d2scene.LineTo(+, +),
			d2scene.CubicTo(+, +, , +, , +-),
			d2scene.LineTo(, +),
			d2scene.ClosePath(),
		)
	} else {
		 = append(,
			d2scene.LineTo(+, +),
			d2scene.LineTo(, +),
			d2scene.ClosePath(),
		)
	}
	return d2scene.Path{Fill: , Commands: }
}

func structuredTextNode(,  string,  d2scene.Point,  d2scene.TextAnchor,  d2scene.Font,  d2scene.Paint,  bool,  d2scene.Box) *d2scene.Node {
	 := d2scene.NewNode(d2scene.TextRun{
		Text: , Origin: , Anchor: , Font: , Fill: , Underline: , Ink: .Bounds(),
	})
	.ID = 
	return 
}

func sceneBoxToGeo( d2scene.Box) *geo.Box {
	return geo.NewBox(geo.NewPoint(.X, .Y), .Width, .Height)
}