package fontface

import (
	
	
	

	
)

const (
	maxTrustedCOLRv1PaintNodes    = 8_192
	maxTrustedCOLRv1Depth         = 32
	maxTrustedCOLRv1Layers        = 256
	maxTrustedCOLRv1GradientStops = 32
)

// trustedCOLRv1Limits are fixed safety ceilings for bundled compilation. Tests
// can lower a ceiling to prove each accounting path rejects before exceeding it.
type trustedCOLRv1Limits struct {
	MaxPaintNodes    int
	MaxDepth         int
	MaxLayers        int
	MaxGradientStops int
}

// bundledNotoColorEmojiCOLRv1Limits leave headroom above the pinned font's
// known maximum graph:
// 6,665 paint nodes, depth 10, and 255 layers in one group.
func bundledNotoColorEmojiCOLRv1Limits() trustedCOLRv1Limits {
	return trustedCOLRv1Limits{
		MaxPaintNodes:    maxTrustedCOLRv1PaintNodes,
		MaxDepth:         maxTrustedCOLRv1Depth,
		MaxLayers:        maxTrustedCOLRv1Layers,
		MaxGradientStops: maxTrustedCOLRv1GradientStops,
	}
}

func ( trustedCOLRv1Limits) () error {
	if .MaxPaintNodes <= 0 || .MaxDepth <= 0 || .MaxLayers <= 0 || .MaxGradientStops <= 0 {
		return fmt.Errorf("d2fonts: every COLRv1 limit must be positive")
	}
	return nil
}

// COLRv1Plan is a renderer-neutral, fully palette-resolved paint graph for one
// glyph. Coordinates remain in the font's design units. Renderers choose their
// own outline rasterizer, transform convention, and temporary-layer strategy.
type COLRv1Plan struct {
	GlyphID uint32
	Root    COLRv1Paint
	Clip    *COLRv1ClipBox
	Usage   COLRv1PlanUsage
}

type COLRv1PlanUsage struct {
	PaintNodes       int
	MaxDepth         int
	MaxLayers        int
	MaxGradientStops int
}

// COLRv1Paint is the closed set of paint operations emitted by the trusted
// compiler.
type COLRv1Paint interface{ isCOLRv1Paint() }

type COLRv1Layers struct{ Paints []COLRv1Paint }
type COLRv1Solid struct{ Color color.NRGBA }
type COLRv1LinearGradient struct {
	ColorLine COLRv1ColorLine
	X0, Y0    float64
	X1, Y1    float64
	X2, Y2    float64
}
type COLRv1RadialGradient struct {
	ColorLine       COLRv1ColorLine
	X0, Y0, Radius0 float64
	X1, Y1, Radius1 float64
}
type COLRv1Glyph struct {
	GlyphID uint32
	Paint   COLRv1Paint
}
type COLRv1Transform struct {
	Matrix COLRv1Affine
	Paint  COLRv1Paint
}
type COLRv1Composite struct {
	Source, Backdrop COLRv1Paint
	Mode             COLRv1CompositeMode
}

func (COLRv1Layers) ()         {}
func (COLRv1Solid) ()          {}
func (COLRv1LinearGradient) () {}
func (COLRv1RadialGradient) () {}
func (COLRv1Glyph) ()          {}
func (COLRv1Transform) ()      {}
func (COLRv1Composite) ()      {}

type COLRv1Affine struct{ Xx, Yx, Xy, Yy, Dx, Dy float64 }

type COLRv1ClipBox struct{ XMin, YMin, XMax, YMax float64 }

type COLRv1ColorStop struct {
	Offset float64
	Color  color.NRGBA
}

type COLRv1ColorLine struct {
	Stops []COLRv1ColorStop
}

type COLRv1CompositeMode uint8

const (
	COLRv1CompositeSrcIn COLRv1CompositeMode = iota
	COLRv1CompositeSoftLight
)

// COLRv1UntrustedFontError says that a caller attempted to use the compiler
// for arbitrary external font data.
type COLRv1UntrustedFontError struct{}

func (COLRv1UntrustedFontError) () string {
	return "d2fonts: COLRv1 font is not the parser-authenticated bundled resource"
}

type COLRv1LimitError struct {
	Limit          string
	Value, Maximum int
}

func ( *COLRv1LimitError) () string {
	return fmt.Sprintf("d2fonts: COLRv1 %s %d exceeds limit %d", .Limit, .Value, .Maximum)
}

type COLRv1UnsupportedPaintError struct{ Type string }

func ( *COLRv1UnsupportedPaintError) () string {
	return fmt.Sprintf("d2fonts: unsupported COLRv1 paint %s", .Type)
}

type COLRv1UnsupportedCompositeError struct{ Mode uint8 }

func ( *COLRv1UnsupportedCompositeError) () string {
	return fmt.Sprintf("d2fonts: unsupported COLRv1 composite mode %d", .Mode)
}

type COLRv1UnsupportedExtendError struct{ Extend uint8 }

func ( *COLRv1UnsupportedExtendError) () string {
	return fmt.Sprintf("d2fonts: unsupported COLRv1 gradient extend %d", .Extend)
}

// CompileBundledNotoColorEmojiCOLRv1Plan compiles the static COLRv1 subset used
// by D2's bundled Noto Color Emoji asset. A false result means the glyph has no
// COLR paint. Parser-issued provenance is checked before inspecting the color
// table; arbitrary external COLRv1 fonts remain unsupported and callers cannot
// replace the safety ceilings.
func ( *ParsedFace) ( uint32) (*COLRv1Plan, bool, error) {
	return .compileBundledNotoColorEmojiCOLRv1Plan(, bundledNotoColorEmojiCOLRv1Limits())
}

func ( *ParsedFace) ( uint32,  trustedCOLRv1Limits) ( *COLRv1Plan,  bool,  error) {
	if  := .validate();  != nil {
		return nil, false, 
	}
	if  == nil || .Outline == nil || .Shaping == nil {
		return nil, false, fmt.Errorf("d2fonts: nil parsed font face")
	}
	if !.hasParsedSource(bundledNotoColorEmojiCOLRv1SHA256) {
		return nil, false, COLRv1UntrustedFontError{}
	}
	return compilePrivateBundledNotoColorEmojiCOLRv1Plan(, )
}

// compileCOLRv1Plan compiles only from the package-private bundled-font parse.
// The caller's ParsedFace authenticates how it obtained the face, never as the
// source of COLR or CPAL tables.
func compileCOLRv1Plan( *ParsedFace,  uint32,  trustedCOLRv1Limits) ( *COLRv1Plan,  bool,  error) {
	if  == nil || .Outline == nil || .Shaping == nil {
		return nil, false, fmt.Errorf("d2fonts: nil private bundled font face")
	}
	// The parser dependency is not part of D2's trusted API surface. Contain any
	// unexpected table-resolution panic so a dependency regression fails the
	// export instead of terminating the process.
	defer func() {
		if recover() != nil {
			 = nil
			 = false
			 = fmt.Errorf("d2fonts: COLRv1 glyph %d table resolution panicked", )
		}
	}()
	if  == 0 ||  > math.MaxUint16 || int() >= .Outline.NumGlyphs() {
		return nil, false, fmt.Errorf("d2fonts: COLRv1 glyph ID %d is out of range", )
	}
	, ,  := searchCOLRPaint(.Shaping.COLR, tables.GlyphID())
	if  != nil {
		return nil, false, fmt.Errorf("d2fonts: COLRv1 glyph %d lookup: %w", , )
	}
	if ! {
		return nil, false, nil
	}
	 := colrv1Builder{face: , limits: }
	,  := .paint(, 1)
	if  != nil {
		return nil, false, fmt.Errorf("d2fonts: COLRv1 glyph %d: %w", , )
	}
	,  := .clip(tables.GlyphID())
	if  != nil {
		return nil, false, fmt.Errorf("d2fonts: COLRv1 glyph %d: %w", , )
	}
	return &COLRv1Plan{GlyphID: , Root: , Clip: , Usage: .usage}, true, nil
}

type colrv1Builder struct {
	face   *ParsedFace
	limits trustedCOLRv1Limits
	usage  COLRv1PlanUsage
}

func ( *colrv1Builder) ( tables.PaintTable,  int) (COLRv1Paint, error) {
	if  > .limits.MaxDepth {
		return nil, &COLRv1LimitError{"paint depth", , .limits.MaxDepth}
	}
	.usage.PaintNodes++
	if .usage.PaintNodes > .limits.MaxPaintNodes {
		return nil, &COLRv1LimitError{"paint node count", .usage.PaintNodes, .limits.MaxPaintNodes}
	}
	.usage.MaxDepth = max(.usage.MaxDepth, )
	if  == nil {
		return nil, fmt.Errorf("d2fonts: COLRv1 nil paint")
	}
	switch p := .(type) {
	case tables.PaintColrLayers:
		if int(.NumLayers) > .limits.MaxLayers {
			return nil, &COLRv1LimitError{"layer count", int(.NumLayers), .limits.MaxLayers}
		}
		,  := .face.Shaping.COLR.LayerList.Resolve()
		if  != nil {
			return nil, fmt.Errorf("d2fonts: COLRv1 layer range: %w", )
		}
		.usage.MaxLayers = max(.usage.MaxLayers, len())
		 := COLRv1Layers{Paints: make([]COLRv1Paint, 0, len())}
		for ,  := range  {
			,  := .(, +1)
			if  != nil {
				return nil, 
			}
			.Paints = append(.Paints, )
		}
		return , nil
	case tables.PaintSolid:
		,  := .color(.PaletteIndex, .Alpha)
		if  != nil {
			return nil, 
		}
		return COLRv1Solid{Color: }, nil
	case tables.PaintLinearGradient:
		,  := .colorLine(.ColorLine)
		if  != nil {
			return nil, 
		}
		return COLRv1LinearGradient{, float64(.X0), float64(.Y0), float64(.X1), float64(.Y1), float64(.X2), float64(.Y2)}, nil
	case tables.PaintRadialGradient:
		,  := .colorLine(.ColorLine)
		if  != nil {
			return nil, 
		}
		return COLRv1RadialGradient{, float64(.X0), float64(.Y0), float64(.Radius0), float64(.X1), float64(.Y1), float64(.Radius1)}, nil
	case tables.PaintGlyph:
		if int(.GlyphID) >= .face.Outline.NumGlyphs() {
			return nil, fmt.Errorf("d2fonts: COLRv1 paint glyph ID %d is out of range", .GlyphID)
		}
		,  := .(.Paint, +1)
		if  != nil {
			return nil, 
		}
		return COLRv1Glyph{GlyphID: uint32(.GlyphID), Paint: }, nil
	case tables.PaintTransform:
		,  := .(.Paint, +1)
		if  != nil {
			return nil, 
		}
		 := .Transform
		return COLRv1Transform{Matrix: COLRv1Affine{float64(.Xx), float64(.Yx), float64(.Xy), float64(.Yy), float64(.Dx), float64(.Dy)}, Paint: }, nil
	case tables.PaintTranslate:
		,  := .(.Paint, +1)
		if  != nil {
			return nil, 
		}
		return COLRv1Transform{Matrix: COLRv1Affine{Xx: 1, Yy: 1, Dx: float64(.Dx), Dy: float64(.Dy)}, Paint: }, nil
	case tables.PaintScale:
		,  := .(.Paint, +1)
		if  != nil {
			return nil, 
		}
		return COLRv1Transform{Matrix: COLRv1Affine{Xx: fixed214(.ScaleX), Yy: fixed214(.ScaleY)}, Paint: }, nil
	case tables.PaintScaleAroundCenter:
		,  := .(.Paint, +1)
		if  != nil {
			return nil, 
		}
		,  := fixed214(.ScaleX), fixed214(.ScaleY)
		return COLRv1Transform{Matrix: COLRv1Affine{Xx: , Yy: , Dx: float64(.CenterX) * (1 - ), Dy: float64(.CenterY) * (1 - )}, Paint: }, nil
	case tables.PaintComposite:
		,  := colrv1CompositeMode(.CompositeMode)
		if  != nil {
			return nil, 
		}
		,  := .(.BackdropPaint, +1)
		if  != nil {
			return nil, 
		}
		,  := .(.SourcePaint, +1)
		if  != nil {
			return nil, 
		}
		return COLRv1Composite{Source: , Backdrop: , Mode: }, nil
	default:
		return nil, &COLRv1UnsupportedPaintError{Type: fmt.Sprintf("%T", )}
	}
}

func ( *colrv1Builder) ( tables.ColorLine) (COLRv1ColorLine, error) {
	if len(.ColorStops) == 0 {
		return COLRv1ColorLine{}, fmt.Errorf("d2fonts: COLRv1 gradient has no stops")
	}
	// The pinned font currently uses only ExtendPad. Repeat and Reflect are
	// defined over the interval from the first to the last color stop, which is
	// not interchangeable with SVG's fixed [0,1] spread interval.
	if .Extend != tables.ExtendPad {
		return COLRv1ColorLine{}, &COLRv1UnsupportedExtendError{Extend: uint8(.Extend)}
	}
	if len(.ColorStops) > .limits.MaxGradientStops {
		return COLRv1ColorLine{}, &COLRv1LimitError{"gradient stop count", len(.ColorStops), .limits.MaxGradientStops}
	}
	.usage.MaxGradientStops = max(.usage.MaxGradientStops, len(.ColorStops))
	 := COLRv1ColorLine{Stops: make([]COLRv1ColorStop, 0, len(.ColorStops))}
	for ,  := range .ColorStops {
		,  := .color(.PaletteIndex, .Alpha)
		if  != nil {
			return COLRv1ColorLine{}, 
		}
		.Stops = append(.Stops, COLRv1ColorStop{Offset: fixed214(.StopOffset), Color: })
	}
	return , nil
}

func ( *colrv1Builder) ( uint16,  tables.Fixed214) (color.NRGBA, error) {
	if len(.face.Shaping.CPAL) == 0 {
		return color.NRGBA{}, fmt.Errorf("d2fonts: COLRv1 has no CPAL palette")
	}
	 := .face.Shaping.CPAL[0]
	if int() >= len() {
		return color.NRGBA{}, fmt.Errorf("d2fonts: COLRv1 palette index %d is out of range", )
	}
	 := fixed214()
	if  < 0 ||  > 1 {
		return color.NRGBA{}, fmt.Errorf("d2fonts: COLRv1 alpha %g is outside [0,1]", )
	}
	 := []
	return color.NRGBA{R: .Red, G: .Green, B: .Blue, A: uint8(math.Round(float64(.Alpha) * ))}, nil
}

func ( *colrv1Builder) ( tables.GlyphID) (*COLRv1ClipBox, error) {
	,  := .face.Shaping.COLR.ClipList.Search()
	if ! {
		return nil, nil
	}
	,  := .(tables.ClipBoxFormat1)
	if ! {
		return nil, &COLRv1UnsupportedPaintError{Type: fmt.Sprintf("%T clip box", )}
	}
	if .XMin > .XMax || .YMin > .YMax {
		return nil, fmt.Errorf("d2fonts: COLRv1 invalid clip box")
	}
	return &COLRv1ClipBox{float64(.XMin), float64(.YMin), float64(.XMax), float64(.YMax)}, nil
}

func fixed214( tables.Fixed214) float64 { return float64() / (1 << 14) }

func colrv1CompositeMode( tables.CompositeMode) (COLRv1CompositeMode, error) {
	switch  {
	case tables.CompositeSrcIn:
		return COLRv1CompositeSrcIn, nil
	case tables.CompositeSoftLight:
		return COLRv1CompositeSoftLight, nil
	default:
		return 0, &COLRv1UnsupportedCompositeError{Mode: uint8()}
	}
}