package d2scenebuild

import (
	
	
	
	
	

	
	
	
	
	
	
)

const (
	appendixPadTop     = 50
	appendixPadSides   = 40
	appendixSpacer     = 20
	appendixFontSize   = 16
	appendixIconRadius = 16
)

type appendixRow struct {
	number     int
	text       string
	y          int
	width      int
	height     int
	lineHeight float64
}

type appendixItem struct {
	object string
	field  string
	text   string
}

type appendixLayout struct {
	topLeft     d2target.Point
	bottomRight d2target.Point
	maxWidth    int
	height      int
	rows        []appendixRow
}

// addAppendixItem retains one static representation of metadata that a raster
// container cannot expose interactively. Shape badges are built separately,
// but connection tooltips and Markdown link titles still receive rows so they
// cannot silently disappear from PNG, GIF, PDF, or PPTX pixels.
func ( *builder) (, ,  string) error {
	if !.options.Appendix ||  == "" {
		return nil
	}
	 := .options.LinkBudget
	if .MaxRegions <= 0 || .MaxStringBytes <= 0 {
		// compileLinkRegions/addMarkdownLinkRegion reports the canonical missing
		// metadata-budget error before a document can be returned.
		return nil
	}
	 := .MaxRegions
	if  <= int(^uint(0)>>1)/2 {
		 *= 2
	} else {
		 = int(^uint(0) >> 1)
	}
	if len(.appendixItems) >=  {
		return fmt.Errorf("scene: appendix item count exceeds limit %d", )
	}
	if  := validateLinkString(.ctx, , , );  != nil {
		return 
	}
	if len() > .MaxStringBytes-.appendixStringBytes {
		return fmt.Errorf("scene: appendix string bytes exceed limit %d", .MaxStringBytes)
	}
	.appendixItems = append(.appendixItems, appendixItem{object: , field: , text: })
	.appendixStringBytes += len()
	return nil
}

// preflightAppendix charges both the numbered badges painted on shapes and
// the rendered appendix strings to the caller's typed metadata budget. A
// shape occupies one link region but may paint both a tooltip and a link
// badge, hence the bounded two-to-one item allowance.
func ( *builder) () error {
	if !.options.Appendix {
		return nil
	}
	 := .options.LinkBudget
	// compileLinkRegions reports the canonical missing-budget error after all
	// feature preflights have run. Avoid replacing it with a less useful
	// appendix-specific limit error.
	if .MaxRegions <= 0 || .MaxStringBytes <= 0 {
		return nil
	}
	 := .MaxRegions
	if  <= int(^uint(0)>>1)/2 {
		 *= 2
	} else {
		 = int(^uint(0) >> 1)
	}
	,  := 0, 0
	for ,  := range .diagram.Shapes {
		if  := .ctx.Err();  != nil {
			return 
		}
		for ,  := range []struct {
			 string
			 string
			 bool
		}{
			{: "tooltip", : .Tooltip, : .Tooltip != ""},
			{: "prettyLink", : .PrettyLink, : .Link != ""},
		} {
			if . {
				++
				if  >  {
					return fmt.Errorf("scene: appendix item count exceeds limit %d", )
				}
			}
			if . == "" {
				continue
			}
			 := fmt.Sprintf("shape[%d] %q", , .ID)
			if  := validateLinkString(.ctx, , ., .);  != nil {
				return 
			}
			if len(.) > .MaxStringBytes- {
				return fmt.Errorf("scene: appendix string bytes exceed limit %d", .MaxStringBytes)
			}
			 += len(.)
		}
	}
	return .ctx.Err()
}

func ( *builder) (,  d2target.Point) (*appendixLayout, error) {
	if !.options.Appendix {
		return nil, nil
	}
	if  := .ctx.Err();  != nil {
		return nil, 
	}
	if len(.appendixItems) == 0 {
		return nil, nil
	}
	,  := textmeasure.NewRuler()
	if  != nil {
		return nil, fmt.Errorf("scene: appendix text ruler: %w", )
	}
	if  == nil {
		return nil, fmt.Errorf("scene: appendix text ruler is nil")
	}

	 := &appendixLayout{topLeft: , bottomRight: }
	 := 0
	 := 1
	for ,  := range .appendixItems {
		if  := .ctx.Err();  != nil {
			return nil, 
		}
		 := d2graph.GetTextDimensions(nil, , &d2target.MText{
			Text: .text, FontSize: appendixFontSize,
		}, nil)
		if  == nil || .Width < 0 || .Height < 0 {
			return nil, fmt.Errorf("scene: %s appendix %s has invalid measured text dimensions", .object, .field)
		}
		 := maxInt(.Height, appendixIconRadius*2)
		,  := checkedAppendixInt(int64(), appendixPadTop*2)
		if ! {
			return nil, fmt.Errorf("scene: appendix row offset exceeds the platform integer domain")
		}
		,  := checkedAppendixInt(int64(.Y), )
		if ! {
			return nil, fmt.Errorf("scene: appendix row y position exceeds the platform integer domain")
		}
		if ,  := checkedAppendixInt(int64(), 5); ! {
			return nil, fmt.Errorf("scene: appendix row text baseline exceeds the platform integer domain")
		}
		,  := checkedAppendixInt(int64(.Width), appendixIconRadius*3)
		if ! {
			return nil, fmt.Errorf("scene: appendix row width exceeds the platform integer domain")
		}
		.rows = append(.rows, appendixRow{
			number: , text: .text, y: , width: , height: ,
			lineHeight: float64(.Height) / float64(maxInt(1, strings.Count(.text, "\n")+1)),
		})
		.maxWidth = maxInt(.maxWidth, )
		,  := checkedAppendixInt(int64(), appendixSpacer)
		if ! {
			return nil, fmt.Errorf("scene: appendix row height exceeds the platform integer domain")
		}
		,  = checkedAppendixInt(int64(), )
		if ! {
			return nil, fmt.Errorf("scene: appendix total height exceeds the platform integer domain")
		}
		++
	}
	if len(.rows) == 0 {
		return nil, nil
	}
	var  bool
	.height,  = checkedAppendixInt(int64(), appendixSpacer)
	if ! {
		return nil, fmt.Errorf("scene: appendix total height exceeds the platform integer domain")
	}
	return , .ctx.Err()
}

func checkedAppendixInt( int64,  int) (int, bool) {
	 := int64(^uint(0) >> 1)
	 := - - 1
	,  := checkedAdd(, int64(), , )
	return int(), 
}

// expandViewBox mirrors appendix.Append's post-serialization arithmetic. Its
// x/y origin remains unchanged; width is widened against absolute row text
// geometry and height always grows by the row block plus PAD_TOP.
func ( *appendixLayout) (, , ,  int) (int, int, int, int, error) {
	if  == nil {
		return , , , , nil
	}
	 := int64(^uint(0) >> 1)
	 := - - 1
	,  := checkedSub(int64(.maxWidth), int64(), , )
	if  {
		,  = checkedAdd(, appendixPadSides*2, , )
	}
	if ! {
		return 0, 0, 0, 0, fmt.Errorf("scene: appendix width expansion exceeds the platform integer domain")
	}
	 := int64()
	if  <  {
		 = 
	}
	,  := checkedAdd(int64(.height), appendixPadTop, , )
	if  {
		,  = checkedAdd(int64(), , , )
	}
	 := 
	if ! ||  < 0 ||  < 0 {
		return 0, 0, 0, 0, fmt.Errorf("scene: appendix height expansion exceeds the platform integer domain")
	}
	return , , int(), int(), nil
}

func ( *builder) ( *appendixLayout,  d2target.Shape, , ,  int) ([]*d2scene.Node, error) {
	if  == nil || .Tooltip == "" && .Link == "" {
		return nil, nil
	}
	, ,  := appendixIconCenters()
	if  != nil {
		return nil, fmt.Errorf("scene: shape[%d] %q appendix icons: %w", , .ID, )
	}
	 := make([]*d2scene.Node, 0, 2)
	if .Tooltip != "" && .TooltipPosition == "" {
		,  := .buildNumberedAppendixIcon(fmt.Sprintf("appendix:shape:%d:tooltip", ), , *)
		if  != nil {
			return nil, 
		}
		 = append(, )
	}
	if .Link != "" {
		,  := .buildNumberedAppendixIcon(fmt.Sprintf("appendix:shape:%d:link", ), , *)
		if  != nil {
			return nil, 
		}
		 = append(, )
	}
	return , nil
}

func appendixIconCenters( d2target.Shape) (,  *d2scene.Point,  error) {
	 := targetGeometry()
	if  == nil {
		return nil, nil, fmt.Errorf("shape geometry is nil")
	}
	 := .Tooltip != "" && .Link != ""
	 := geo.NewPoint(float64(.Pos.X+.Width), float64(.Pos.Y))
	 := geo.NewPoint(
		float64(.Pos.X)+float64(.Width)/2,
		float64(.Pos.Y)+float64(.Height)/2,
	)
	 := geo.NewVector(-2*appendixIconRadius, 0)
	 := false
	switch .GetType() {
	case shape.STEP_TYPE, shape.HEXAGON_TYPE, shape.QUEUE_TYPE, shape.PAGE_TYPE:
		.Y = float64(.Pos.Y)
	case shape.PACKAGE_TYPE:
		.X = float64(.Pos.X + .Width)
	case shape.CIRCLE_TYPE, shape.OVAL_TYPE, shape.DIAMOND_TYPE,
		shape.PERSON_TYPE, shape.CLOUD_TYPE, shape.CYLINDER_TYPE:
		if  {
			 = true
			 = .AddVector()
		}
	}
	 := .VectorTo()
	 := shape.TraceToShapeBorder(, , .AddVector())
	if  == nil || !finite(.X) || !finite(.Y) {
		return nil, nil, fmt.Errorf("shape border trace did not resolve to a finite point")
	}
	 := 
	if  {
		if  {
			 = .AddVector(.Reverse())
			,  = , 
		} else {
			 = .AddVector()
		}
	}
	 = &d2scene.Point{X: math.Ceil(.X), Y: math.Ceil(.Y)}
	 = &d2scene.Point{X: math.Ceil(.X), Y: math.Ceil(.Y)}
	return , , nil
}

func ( *builder) ( string,  int,  d2scene.Point) (*d2scene.Node, error) {
	,  := .paint("#ffffff", +" fill")
	if  != nil {
		return nil, 
	}
	,  := .paint("#DEE1EB", +" border")
	if  != nil {
		return nil, 
	}
	,  := .paint("#000000", +" number")
	if  != nil {
		return nil, 
	}
	,  := .font(d2target.Text{FontSize: appendixFontSize, Bold: true})
	if  != nil {
		return nil, fmt.Errorf("scene: %s: %w", , )
	}
	 := d2scene.NewNode(nil)
	.ID = 
	.Classes = []string{"appendix-icon"}
	.Filters = []d2scene.Filter{d2scene.DropShadow{
		SigmaX: 32, SigmaY: 32, Color: color.NRGBA{R: 31, G: 36, B: 58, A: 26},
	}}
	 := d2scene.NewNode(d2scene.Ellipse{
		Center: , RadiusX: appendixIconRadius, RadiusY: appendixIconRadius, Fill: ,
		Stroke: &d2scene.Stroke{Paint: , Width: 1, Cap: d2scene.CapButt, Join: d2scene.JoinMiter, MiterLimit: 4},
	})
	.ID =  + ":circle"
	 := .Y + 5
	 := strconv.Itoa()
	 := d2scene.NewNode(d2scene.TextRun{
		Text: , Origin: d2scene.Point{X: .X, Y: }, Anchor: d2scene.AnchorMiddle,
		Font: , Fill: ,
		Ink: d2scene.NewBounds(.X-appendixIconRadius, .Y-appendixIconRadius, .X+appendixIconRadius, .Y+appendixIconRadius),
	})
	.ID =  + ":number"
	.Children = []*d2scene.Node{, }
	return , nil
}

func ( *builder) ( *appendixLayout) (*d2scene.Node, error) {
	if  == nil {
		return nil, nil
	}
	,  := .paint("B2", "appendix separator")
	if  != nil {
		return nil, 
	}
	,  := .paint("N1", "appendix text")
	if  != nil {
		return nil, 
	}
	,  := .font(d2target.Text{FontSize: appendixFontSize})
	if  != nil {
		return nil, fmt.Errorf("scene: appendix text: %w", )
	}
	 := d2scene.NewNode(nil)
	.ID = "appendix"
	.Classes = []string{"appendix"}
	,  := checkedAppendixInt(int64(.bottomRight.Y), appendixPadTop)
	if ! {
		return nil, fmt.Errorf("scene: appendix separator y exceeds the platform integer domain")
	}
	,  := checkedAppendixInt(int64(.topLeft.X), -appendixPadSides)
	if ! {
		return nil, fmt.Errorf("scene: appendix separator left edge exceeds the platform integer domain")
	}
	,  := checkedAppendixInt(int64(maxInt(.maxWidth, .bottomRight.X)), appendixPadSides)
	if ! {
		return nil, fmt.Errorf("scene: appendix separator right edge exceeds the platform integer domain")
	}
	 := d2scene.NewNode(d2scene.Path{
		Commands: []d2scene.PathCommand{
			d2scene.MoveTo(float64(), float64()),
			d2scene.LineTo(float64(), float64()),
		},
		Stroke: &d2scene.Stroke{Paint: , Width: 1, Cap: d2scene.CapButt, Join: d2scene.JoinMiter, MiterLimit: 4},
	})
	.ID = "appendix:separator"
	.Children = append(.Children, )
	for ,  := range .rows {
		if  := .ctx.Err();  != nil {
			return nil, 
		}
		 := d2scene.NewNode(nil)
		.ID = fmt.Sprintf("appendix:row:%d", .number)
		,  := .buildNumberedAppendixIcon(.ID+":icon", .number, d2scene.Point{X: appendixIconRadius, Y: float64(.y)})
		if  != nil {
			return nil, 
		}
		.Children = append(.Children, )
		 := strings.Split(.text, "\n")
		for ,  := range  {
			if  := .ctx.Err();  != nil {
				return nil, 
			}
			 := legendRenderedText()
			if  == "" && len() > 1 {
				 = " "
			}
			 := float64(.y) + 5 + float64()*.lineHeight
			 := float64(.y) + 5 - float64(.height) + float64()*.lineHeight
			 :=  + math.Max(1, .lineHeight)
			 := d2scene.NewNode(d2scene.TextRun{
				Text: , Origin: d2scene.Point{X: appendixIconRadius * 3, Y: }, Anchor: d2scene.AnchorStart,
				Font: , Fill: ,
				Ink: d2scene.NewBounds(appendixIconRadius*3, , float64(maxInt(appendixIconRadius*3+1, .width)), ),
			})
			.ID = fmt.Sprintf("%s:text:%d", .ID, )
			.Children = append(.Children, )
		}
		.Children = append(.Children, )
	}
	return , .ctx.Err()
}

func appendixIconNumbers( *d2target.Diagram) (,  []int) {
	 = make([]int, len(.Shapes))
	 = make([]int, len(.Shapes))
	 := 1
	for ,  := range .Shapes {
		if .Tooltip != "" {
			[] = 
			++
		}
		if .Link != "" {
			[] = 
			++
		}
	}
	return , 
}