package d2svgimport

import (
	
	
	
	
	

	
)

var stopPresentationProperties = map[string]struct{}{
	"stop-color":   {},
	"stop-opacity": {},
}

func isGradientResourceElement( string) bool {
	return  == "linearGradient" ||  == "stop"
}

// validateGradientTree keeps paint servers out of the rendering tree. The
// supported subset deliberately has no resource-to-resource edges: gradients
// are direct children of <defs>, stops are direct children of a gradient, and
// href inheritance is rejected by the exact attribute allowlist.
func ( *svgImporter) ( *svgElement) error {
	type  struct {
		 *svgElement
		  *svgElement
	}
	 := []{{: }}
	for len() != 0 {
		if  := .ctx.Err();  != nil {
			return 
		}
		 := [len()-1]
		 = [:len()-1]
		switch ..name {
		case "linearGradient":
			if . == nil || ..name != "defs" {
				return .errorf("element <linearGradient> must be a direct child of <defs>")
			}
			if ..id == "" {
				return .errorf("element <linearGradient> must declare an id")
			}
			if len(..children) == 0 {
				return .errorf("element <linearGradient> must contain at least one <stop>")
			}
			for ,  := range ..children {
				if .name != "stop" {
					return .errorf("element <linearGradient> has unsupported child <%s>", displayXMLName(.name))
				}
			}
		case "stop":
			if . == nil || ..name != "linearGradient" {
				return .errorf("element <stop> is only supported inside <linearGradient>")
			}
			if len(..children) != 0 {
				return .errorf("element <stop> cannot contain child elements")
			}
		}
		for  := len(..children) - 1;  >= 0; -- {
			 = append(, {: ..children[], : .})
		}
	}
	return nil
}

func ( *svgImporter) ( *svgElement) error {
	 := elementAttributes[.name]
	for ,  := range .attrOrder {
		if  := .ctx.Err();  != nil {
			return 
		}
		if ,  := []; ! {
			return .errorf("element <%s> has unsupported attribute %q", .name, displayXMLName())
		}
	}

	switch .name {
	case "linearGradient":
		return .compileLinearGradient()
	case "stop":
		return .compileGradientStop()
	default:
		return .errorf("internal unsupported gradient element <%s>", .name)
	}
}

func ( *svgImporter) ( *svgElement) error {
	for ,  := range []string{"x1", "y1", "x2", "y2", "gradientUnits"} {
		if ,  := .attrs[]; ! {
			return .errorf("element <linearGradient> is missing required attribute %q", )
		}
	}
	,  := trimSVGSpace(.ctx, .attrs["gradientUnits"])
	if  != nil {
		return 
	}
	if  != "userSpaceOnUse" {
		return .errorf("element <linearGradient> supports only gradientUnits=\"userSpaceOnUse\"")
	}

	 := func( string) (float64, error) {
		,  := parseSVGLength(.attrs[], true)
		if  != nil {
			return 0, .propertyError(, , )
		}
		return , nil
	}
	,  := ("x1")
	if  != nil {
		return 
	}
	,  := ("y1")
	if  != nil {
		return 
	}
	,  := ("x2")
	if  != nil {
		return 
	}
	,  := ("y2")
	if  != nil {
		return 
	}
	 := d2scene.Point{X: , Y: }
	 := d2scene.Point{X: , Y: }
	if  ==  {
		return .errorf("element <linearGradient> has a zero-length gradient vector")
	}

	if ,  := .attrs["spreadMethod"];  {
		,  := trimSVGSpace(.ctx, )
		if  != nil {
			return 
		}
		if  != "pad" {
			return .errorf("element <linearGradient> supports only the pad spread method")
		}
	}

	 := d2scene.Identity()
	if ,  := .attrs["gradientTransform"];  {
		,  := trimSVGSpace(.ctx, )
		if  != nil {
			return 
		}
		if  == "" {
			return .errorf("element <linearGradient> has an empty gradientTransform")
		}
		 := .limits.MaxTransformFunctions - .parsedTransforms
		if  <= 0 {
			return .errorf("transform function count exceeds limit %d", .limits.MaxTransformFunctions)
		}
		var  int
		, ,  = parseTransformWithCount(.ctx, .source, , transformLimits{
			MaxBytes: .limits.MaxAttributeBytes, MaxFunctions: ,
		})
		if  != nil {
			return 
		}
		.parsedTransforms += 
	}
	if !affineInverseIsFinite() {
		return .errorf("element <linearGradient> has a singular or unrepresentable gradientTransform")
	}
	 := .Vector(d2scene.Point{X: .X - .X, Y: .Y - .Y})
	if math.IsNaN(.X) || math.IsInf(.X, 0) || math.IsNaN(.Y) || math.IsInf(.Y, 0) ||
		 == (d2scene.Point{}) {
		return .errorf("element <linearGradient> has invalid transformed gradient geometry")
	}

	.gradient = &d2scene.LinearGradient{
		Start: , End: , Units: d2scene.UserSpaceOnUse,
		Transform: , Spread: d2scene.SpreadPad,
	}
	return nil
}

func ( *svgImporter) ( *svgElement) error {
	, ,  := .classTokens()
	if  != nil {
		return 
	}
	.classes = 
	.classSet = 
	,  := .declarationsFor()
	if  != nil {
		return 
	}
	.declarations = 
	for  := range  {
		if ,  := stopPresentationProperties[]; ! {
			return .errorf("element <stop> has unsupported style property %q", )
		}
	}

	,  := .attrs["offset"]
	if ! {
		return .errorf("element <stop> is missing offset")
	}
	,  := parseUnitInterval()
	if  != nil {
		return .propertyError(, "offset", )
	}
	,  := .stopFromDeclarations()
	if  != nil {
		return 
	}
	.Offset = 
	.gradientStop = &
	return nil
}

func ( *svgImporter) ( *svgElement) (d2scene.GradientStop, error) {
	 := color.NRGBA{A: 255}
	if ,  := .declarations["stop-color"];  {
		if equalASCIIEqualFold(, "inherit") || equalASCIIEqualFold(, "currentColor") || strings.Contains(strings.ToLower(), "url(") {
			return d2scene.GradientStop{}, .propertyError(, "stop-color", fmt.Errorf("only an explicit solid color is supported"))
		}
		,  := parseSolidColor()
		if  != nil {
			return d2scene.GradientStop{}, .propertyError(, "stop-color", )
		}
		 = 
	}
	 := 1.0
	if ,  := .declarations["stop-opacity"];  {
		if equalASCIIEqualFold(, "inherit") {
			return d2scene.GradientStop{}, .propertyError(, "stop-opacity", fmt.Errorf("inheritance is unsupported"))
		}
		,  := parseUnitInterval()
		if  != nil {
			return d2scene.GradientStop{}, .propertyError(, "stop-opacity", )
		}
		 = 
	}
	.A = uint8(math.Round(float64(.A) * ))
	return d2scene.GradientStop{Color: }, nil
}

func ( *svgImporter) ( *svgElement) error {
	 := []*svgElement{}
	for len() != 0 {
		if  := .ctx.Err();  != nil {
			return 
		}
		 := [len()-1]
		 = [:len()-1]
		if .name == "linearGradient" {
			if .gradient == nil {
				return .errorf("internal uncompiled linear gradient")
			}
			 := make([]d2scene.GradientStop, len(.children))
			 := -1.0
			for ,  := range .children {
				if  := .ctx.Err();  != nil {
					return 
				}
				if .gradientStop == nil {
					return .errorf("internal uncompiled gradient stop")
				}
				 := *.gradientStop
				if .Offset <  {
					return .errorf("element <linearGradient> has decreasing stop offsets")
				}
				[] = 
				 = .Offset
			}
			.gradient.Stops = 
		}
		for  := len(.children) - 1;  >= 0; -- {
			 = append(, .children[])
		}
	}
	return nil
}

func ( *svgImporter) ( *svgElement) error {
	for  := range .declarations {
		if ,  := stopPresentationProperties[];  {
			return .errorf("element <%s> has unsupported style property %q", .name, )
		}
		if .name == "defs" &&  == "clip-path" {
			return .errorf("element <defs> cannot carry clip-path because it is not an emitted element")
		}
	}
	return nil
}

// validateStylesheetRules validates every class rule, including unmatched
// rules. Stop declarations and ordinary rendering declarations cannot be
// mixed because no supported element accepts both.
func ( *svgImporter) () error {
	for ,  := range .stylesheetRules {
		if  := .ctx.Err();  != nil {
			return 
		}
		 := make(map[string]string)
		 := make(map[string]string)
		for ,  := range .declarations {
			if ,  := stopPresentationProperties[];  {
				[] = 
			} else {
				[] = 
			}
		}
		if len() != 0 && len() != 0 {
			return .errorf("stylesheet rule .%s mixes stop-only and rendering properties", .class)
		}
		if len() != 0 {
			 := &svgElement{name: "stop", declarations: }
			if ,  := .stopFromDeclarations();  != nil {
				return 
			}
			continue
		}
		 := &svgElement{name: "style", declarations: }
		if ,  := .computeStyle(defaultSVGStyle(), );  != nil {
			return 
		}
	}
	return nil
}

func ( *svgImporter) ( string) (d2scene.Paint, error) {
	if !strings.HasPrefix(, "url(#") || !strings.HasSuffix(, ")") {
		return nil, fmt.Errorf("paint servers require one local url(#id) reference; external references and fallbacks are forbidden")
	}
	 := [len("url(#") : len()-1]
	,  := .validLocalPaintID()
	if  != nil {
		return nil, 
	}
	if ! {
		return nil, fmt.Errorf("paint servers require one local url(#id) reference; external references and fallbacks are forbidden")
	}
	 := .ids[]
	if  == nil {
		return nil, fmt.Errorf("paint server references an unknown local id")
	}
	if .name != "linearGradient" || .gradient == nil {
		return nil, fmt.Errorf("paint server local id does not name a supported linear gradient")
	}
	if  := .ctx.Err();  != nil {
		return nil, 
	}
	 := *.gradient
	.Stops = make([]d2scene.GradientStop, len(.gradient.Stops))
	for ,  := range .gradient.Stops {
		if &255 == 0 {
			if  := .ctx.Err();  != nil {
				return nil, 
			}
		}
		.Stops[] = 
	}
	return , nil
}

// validLocalPaintID accepts the bounded unescaped identifier spelling used by
// the corpus. Broader SVG IDs would require a real CSS token/escape parser and
// are rejected instead of being reinterpreted by string slicing.
func ( *svgImporter) ( string) (bool, error) {
	if  == "" {
		return false, nil
	}
	for  := 0;  < len(); ++ {
		if &4095 == 0 {
			if  := .ctx.Err();  != nil {
				return false, 
			}
		}
		 := []
		if  == 0 {
			if !isASCIIAlpha() &&  != '_' {
				return false, nil
			}
			continue
		}
		if !isASCIIAlpha() && ( < '0' ||  > '9') &&
			 != '_' &&  != '-' &&  != '.' &&  != ':' {
			return false, nil
		}
	}
	return true, .ctx.Err()
}

// affineInverseIsFinite uses exact rational arithmetic for the determinant and
// inverse coefficients. Raw float64 determinants can underflow or overflow for
// finite, invertible SVG matrices such as scale(1e300,1e-300).
func affineInverseIsFinite( d2scene.Matrix) bool {
	if !.IsFinite() {
		return false
	}
	 := func( float64) *big.Rat {
		return new(big.Rat).SetFloat64()
	}
	 := func(,  *big.Rat) *big.Rat {
		return new(big.Rat).Mul(, )
	}
	 := func(,  *big.Rat) *big.Rat {
		return new(big.Rat).Sub(, )
	}
	, , ,  := (.A), (.B), (.C), (.D)
	,  := (.E), (.F)
	 := ((, ), (, ))
	if .Sign() == 0 {
		return false
	}
	 := []*big.Rat{
		,
		new(big.Rat).Neg(),
		new(big.Rat).Neg(),
		,
		((, ), (, )),
		((, ), (, )),
	}
	for ,  := range  {
		,  := new(big.Rat).Quo(, ).Float64()
		if math.IsNaN() || math.IsInf(, 0) {
			return false
		}
	}
	return true
}