package d2scenebuild

import (
	
	
	
	

	
	
	
	
)

// buildConnectionMask creates one diagram-wide label mask. A label belonging
// to one object can cover an unrelated connection beneath it.
func ( *builder) ( d2scene.Box) (*d2scene.Mask, error) {
	 := d2scene.SolidPaint{Color: color.NRGBA{A: 0xff}}
	 := d2scene.SolidPaint{Color: color.NRGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}}
	 := make([]*d2scene.Node, 0)

	for ,  := range .diagram.Shapes {
		if  := .ctx.Err();  != nil {
			return nil, 
		}
		 := label.FromString(.LabelPosition)
		if .Label == "" || !.IsBorder() {
			continue
		}
		// Class and table shapes do not participate in the ordinary border-label
		// mask stage.
		if .Type == d2target.ShapeClass || .Type == d2target.ShapeSQLTable {
			continue
		}
		,  := shapeLabelPlacement()
		,  := borderLabelMaskBox(, , .LabelWidth, .LabelHeight, , .StrokeWidth)
		if ! || .Width <= 0 || .Height <= 0 {
			continue
		}
		 := d2scene.NewNode(d2scene.Rect{Box: , Fill: })
		.ID = .ID + ":border-label-mask-hole"
		 = append(, )
	}

	for ,  := range .diagram.Connections {
		if  := .ctx.Err();  != nil {
			return nil, 
		}
		if .Label == "" && .Icon == nil {
			continue
		}
		var  d2scene.Box
		 := .IconPosition
		if .Label != "" {
			 := .GetLabelTopLeft()
			if  == nil {
				return nil, invalidField(fmt.Sprintf("connection %q", .ID), "labelPosition", .LabelPosition, "does not resolve on the route")
			}
			 := math.Round(.X) - 2
			 := .LabelWidth + 4
			if .Icon != nil {
				 -= d2target.CONNECTION_ICON_LABEL_GAP + d2target.DEFAULT_ICON_SIZE
				 += d2target.CONNECTION_ICON_LABEL_GAP + d2target.DEFAULT_ICON_SIZE
			}
			 = d2scene.Box{
				X: , Y: math.Round(.Y),
				Width: float64(), Height: float64(.LabelHeight),
			}
			 = .LabelPosition
		} else {
			 := .GetIconPosition()
			if  == nil {
				return nil, invalidField(fmt.Sprintf("connection %q", .ID), "iconPosition", .IconPosition, "does not resolve on the route")
			}
			 = d2scene.Box{
				X: .X - 2, Y: .Y,
				Width: d2target.DEFAULT_ICON_SIZE + 4, Height: d2target.DEFAULT_ICON_SIZE,
			}
		}
		 := d2scene.NewNode(d2scene.Rect{
			Box:  ,
			Fill: ,
		})
		.ID = .ID + ":label-mask-hole"
		if !label.FromString().IsOnEdge() {
			.Opacity = .75
		}
		 = append(, )
	}

	if len() == 0 {
		return nil, nil
	}
	 := d2scene.NewNode(nil)
	.ID = "connection-label-mask"
	 := d2scene.NewNode(d2scene.Rect{Box: , Fill: })
	.ID = "connection-label-mask:base"
	.Children = append(.Children, )
	.Children = append(.Children, ...)
	return &d2scene.Mask{Type: d2scene.MaskLuminance, Root: , Transform: d2scene.Identity()}, nil
}

func borderLabelMaskBox( label.Position,  *geo.Point, ,  int,  *geo.Box,  int) (d2scene.Box, bool) {
	 := float64()
	var  d2scene.Box
	switch  {
	case label.BorderTopLeft, label.BorderTopCenter, label.BorderTopRight:
		 = d2scene.Box{
			X: .X - 2, Y: .TopLeft.Y - /2,
			Width: float64( + 4), Height: ,
		}
	case label.BorderBottomLeft, label.BorderBottomCenter, label.BorderBottomRight:
		 = d2scene.Box{
			X: .X - 2, Y: .TopLeft.Y + .Height - /2,
			Width: float64( + 4), Height: ,
		}
	case label.BorderLeftTop, label.BorderLeftMiddle, label.BorderLeftBottom:
		 = d2scene.Box{
			X: .TopLeft.X - /2, Y: .Y - 2,
			Width: , Height: float64( + 4),
		}
	case label.BorderRightTop, label.BorderRightMiddle, label.BorderRightBottom:
		 = d2scene.Box{
			X: .TopLeft.X + .Width - /2, Y: .Y - 2,
			Width: , Height: float64( + 4),
		}
	default:
		return d2scene.Box{}, false
	}
	return , true
}

func shapeLabelPlacement( d2target.Shape) (*geo.Box, *geo.Point) {
	 := targetGeometry()
	 := label.FromString(.LabelPosition)
	var  *geo.Box
	if .IsOutside() || .IsBorder() {
		 = .GetBox().Copy()
		if .ThreeDee {
			 := d2target.THREE_DEE_OFFSET
			if .Type == d2target.ShapeHexagon {
				 /= 2
			}
			.TopLeft.Y -= float64()
			.Height += float64()
			.Width += float64(d2target.THREE_DEE_OFFSET)
		} else if .Multiple {
			.TopLeft.Y -= float64(d2target.MULTIPLE_OFFSET)
			.Height += float64(d2target.MULTIPLE_OFFSET)
			.Width += float64(d2target.MULTIPLE_OFFSET)
		}
	} else {
		 = .GetInnerBox()
	}
	 := .GetPointOnBox(, label.PADDING, float64(.LabelWidth), float64(.LabelHeight))
	return , 
}

func ( *builder) ( d2target.Connection) ([]*d2scene.Node, error) {
	 := make([]*d2scene.Node, 0, 5)
	if .Label != "" {
		 := .GetLabelTopLeft()
		if  == nil {
			return nil, invalidField(fmt.Sprintf("connection %q", .ID), "labelPosition", .LabelPosition, "does not resolve on the route")
		}
		 = geo.NewPoint(math.Round(.X), math.Round(.Y))
		if .Language == "latex" {
			,  := .buildLatexLabelNode(
				fmt.Sprintf("connection %q", .ID), .ID+":label:0",
				.Label, .Color, ,
			)
			if  != nil {
				return nil, 
			}
			 = append(, )
		} else if .Language == "markdown" {
			,  := .buildConnectionMarkdown(, )
			if  != nil {
				return nil, 
			}
			 = append(, )
		} else if .Language != "" {
			 := fmt.Sprintf("connection %q", .ID)
			,  := .activeCodeStyle()
			if  != nil {
				return nil, 
			}
			,  := .buildCodeTextRuns(
				, .ID+":code", .Text, ,
				d2scene.Point{X: .X, Y: .Y},
			)
			if  != nil {
				return nil, 
			}
			 = append(, ...)
		} else {
			if .Fill != "" {
				,  := .paint(.Fill, fmt.Sprintf("connection %q label fill", .ID))
				if  != nil {
					return nil, 
				}
				 := d2scene.NewNode(d2scene.Rect{
					Box: d2scene.Box{
						X: .X - 4, Y: .Y - 3,
						Width: float64(.LabelWidth + 8), Height: float64(.LabelHeight + 6),
					},
					RadiusX: 10, RadiusY: 10, Fill: ,
				})
				.ID = .ID + ":label-fill"
				 = append(, )
			}
			,  := .font(.Text)
			if  != nil {
				return nil, fmt.Errorf("scene: connection %q label: %w", .ID, )
			}
			 := .GetFontColor()
			 := .Underline
			if .Link != "" {
				// Linked connection labels are underlined and use unvisited-link blue.
				 = "blue"
				 = true
			}
			,  := .paint(, fmt.Sprintf("connection %q label color", .ID))
			if  != nil {
				return nil, 
			}
			 = append(, centeredTextRuns(
				.ID+":label", .Label, ,
				.LabelWidth, .LabelHeight, .FontSize,
				, , ,
			)...)
		}
	}

	for ,  := range []struct {
		  string
		  *d2target.Text
		 bool
	}{
		{: "src", : .SrcLabel},
		{: "dst", : .DstLabel, : true},
	} {
		if . == nil || ..Label == "" {
			continue
		}
		 := .Text
		.Bold = false
		.Italic = true
		.Underline = false
		// Arrowhead labels use the primary italic face independently of the main
		// connection label's optional mono face.
		.FontFamily = "default"
		,  := .font()
		if  != nil {
			return nil, fmt.Errorf("scene: connection %q %s arrowhead label: %w", .ID, ., )
		}
		 := d2target.FG_COLOR
		if ..Color != "" {
			 = ..Color
		}
		,  := .paint(, fmt.Sprintf("connection %q %s arrowhead label color", .ID, .))
		if  != nil {
			return nil, 
		}
		 := .GetArrowheadLabelPosition(.)
		 = append(, centeredTextRuns(
			fmt.Sprintf("%s:%s-label", .ID, .), ..Label, ,
			..LabelWidth, ..LabelHeight, .FontSize,
			, , false,
		)...)
	}
	return , nil
}

func centeredTextRuns(,  string,  *geo.Point, , ,  int,  d2scene.Font,  d2scene.Paint,  bool) []*d2scene.Node {
	 := strings.Split(, "\n")
	 := float64() / float64(len())
	 := make([]*d2scene.Node, 0, len())
	for ,  := range  {
		 := .Y + float64()*
		 := d2scene.TextRun{
			Text: ,
			Origin: d2scene.Point{
				X: .X + float64()/2,
				Y: .Y + float64() + float64()*,
			},
			Anchor:    d2scene.AnchorMiddle,
			Font:      ,
			Fill:      ,
			Underline: ,
			Ink:       d2scene.NewBounds(.X, , .X+float64(), +),
		}
		 := d2scene.NewNode()
		.ID = fmt.Sprintf("%s:%d", , )
		 = append(, )
	}
	return 
}