package d2raster

import (
	
	
	
	
	

	
)

type preparedPattern struct {
	tile            d2scene.Box
	originModX      float64
	originModY      float64
	deviceToPattern d2scene.Matrix
	tileResource    *preparedPatternTile
	directPixels    bool
	pixelOffsetX    int
	pixelOffsetY    int
}

type preparedPatternTile struct {
	width     int
	height    int
	bounds    image.Rectangle
	tileBytes int64
	root      *preparedNode

	image     *image.RGBA
	rendering bool
}

type preparedPatternTileKey struct {
	root        *d2scene.Node
	width       int
	height      int
	scaleX      float64
	scaleY      float64
	importDepth int
}

func ( *preflight) ( d2scene.Paint,  *color.NRGBA,  d2scene.Box,  d2scene.Matrix,  int) (*preparedPaint, error) {
	return prepareAnimatedPaintWithPattern(, , , , func( d2scene.PatternPaint,  d2scene.Box,  d2scene.Matrix) (*preparedPaint, error) {
		return .preparePatternPaint(, , , )
	})
}

func ( *preflight) ( d2scene.PatternPaint,  d2scene.Box,  d2scene.Matrix,  int) (*preparedPaint, error) {
	if  := .ctx.Err();  != nil {
		return nil, 
	}
	if  := validateBox(.Tile);  != nil {
		return nil, fmt.Errorf("pattern tile: %w", )
	}
	if .Tile.Width == 0 || .Tile.Height == 0 {
		return nil, fmt.Errorf("pattern tile has zero width or height")
	}
	if !finite(.Tile.X+.Tile.Width) || !finite(.Tile.Y+.Tile.Height) {
		return nil, fmt.Errorf("pattern tile endpoints are non-finite")
	}
	if .Root == nil {
		return nil, fmt.Errorf("pattern has no root node")
	}

	, ,  := preparePatternTransform(.Units, .Transform, , )
	if  != nil {
		return nil, fmt.Errorf("pattern: %w", )
	}
	,  := patternTilePixels(.Vector(d2scene.Point{X: .Tile.Width}))
	if  != nil {
		return nil, fmt.Errorf("pattern tile width: %w", )
	}
	,  := patternTilePixels(.Vector(d2scene.Point{Y: .Tile.Height}))
	if  != nil {
		return nil, fmt.Errorf("pattern tile height: %w", )
	}
	 := image.Rect(0, 0, , )
	,  := pixelStorageBytes(, 4)
	if  != nil {
		return nil, fmt.Errorf("pattern tile: %w", )
	}

	 := float64() / .Tile.Width
	 := float64() / .Tile.Height
	if !finite() || !finite() ||  <= 0 ||  <= 0 {
		return nil, fmt.Errorf("pattern tile cannot be represented in raster coordinates")
	}
	 :=  + 1
	 := preparedPatternTileKey{
		root: .Root, width: , height: ,
		scaleX: , scaleY: , importDepth: ,
	}
	if .patternTiles == nil {
		.patternTiles = make(map[preparedPatternTileKey]*preparedPatternTile)
	}
	,  := .patternTiles[]
	if  &&  == nil {
		return nil, fmt.Errorf("pattern root: cyclic pattern resource")
	}
	if ! {
		// A nil sentinel makes definition cycles actionable while the root is
		// being prepared. Remove it on error so a failed preparation cannot
		// poison subsequent diagnostic work in this preflight.
		.patternTiles[] = nil
		 := .frameBounds
		.frameBounds = 
		,  := .node(.Root, d2scene.Scale(, ), 1, )
		.frameBounds = 
		if  != nil {
			delete(.patternTiles, )
			return nil, fmt.Errorf("pattern root: %w", )
		}
		 = &preparedPatternTile{
			width: , height: , bounds: , tileBytes: , root: ,
		}
		.patternTiles[] = 
	}
	, ,  := directPatternPixelOffsets(.Tile, , , )
	return &preparedPaint{
		kind: preparedPatternPaint,
		pattern: &preparedPattern{
			tile:            .Tile,
			originModX:      math.Mod(.Tile.X, .Tile.Width),
			originModY:      math.Mod(.Tile.Y, .Tile.Height),
			deviceToPattern: ,
			tileResource:    ,
			directPixels:    ,
			pixelOffsetX:    ,
			pixelOffsetY:    ,
		},
	}, nil
}

// directPatternPixelOffsets recognizes the common one-device-pixel to
// one-pattern-pixel mapping. Integer translation and tile origins make every
// pixel-center sample land at n+0.5, so wrapping can be performed exactly in
// integer pixel space without a floating-point remainder per channel.
func directPatternPixelOffsets( d2scene.Box,  d2scene.Matrix, ,  int) (int, int, bool) {
	if  <= 0 ||  <= 0 ||
		.Width != float64() || .Height != float64() ||
		.A != 1 || .B != 0 ||
		.C != 0 || .D != 1 {
		return 0, 0, false
	}
	const  = float64(uint64(1) << 52)
	 := [...]float64{.X, .Y, .E, .F}
	for ,  := range  {
		if !finite() || math.Trunc() !=  ||  < - ||  >  {
			return 0, 0, false
		}
	}
	 := func(,  float64,  int) int {
		 := int64()
		 := int64()% - int64()%
		 %= 
		if  < 0 {
			 += 
		}
		return int()
	}
	return (.E, .X, ), (.F, .Y, ), true
}

func preparePatternTransform( d2scene.PaintUnits,  d2scene.Matrix,  d2scene.Box,  d2scene.Matrix) (d2scene.Matrix, d2scene.Matrix, error) {
	if  > d2scene.UserSpaceOnUse {
		return d2scene.Matrix{}, d2scene.Matrix{}, fmt.Errorf("invalid paint units %d", )
	}
	if !.IsFinite() {
		return d2scene.Matrix{}, d2scene.Matrix{}, fmt.Errorf("non-finite pattern transform")
	}
	if !.IsFinite() {
		return d2scene.Matrix{}, d2scene.Matrix{}, fmt.Errorf("non-finite object transform")
	}
	 := d2scene.Identity()
	if  == d2scene.ObjectBoundingBox {
		if  := validateBox();  != nil {
			return d2scene.Matrix{}, d2scene.Matrix{}, fmt.Errorf("invalid object bounding box: %w", )
		}
		if .Width == 0 || .Height == 0 {
			return d2scene.Matrix{}, d2scene.Matrix{}, fmt.Errorf("object bounding box has zero width or height")
		}
		 = d2scene.Translate(.X, .Y).Mul(d2scene.Scale(.Width, .Height))
	}
	 := .Mul().Mul()
	if !.IsFinite() {
		return d2scene.Matrix{}, d2scene.Matrix{}, fmt.Errorf("composed pattern transform is non-finite")
	}
	, ,  := finiteAffineInverse()
	if  != nil || ! {
		return d2scene.Matrix{}, d2scene.Matrix{}, fmt.Errorf("singular pattern transform")
	}
	return , , nil
}

func patternTilePixels( d2scene.Point) (int, error) {
	 := math.Hypot(.X, .Y)
	if !finite() ||  <= 0 {
		return 0, fmt.Errorf("maps outside the finite pixel domain")
	}
	 = math.Ceil()
	 := int(^uint(0) >> 1)
	if !finite() ||  <= 0 ||  >= float64() {
		return 0, fmt.Errorf("exceeds the platform integer domain")
	}
	return int(), nil
}

func ( *preparedPaint) ( context.Context,  *rasterScratch) error {
	if  == nil || .kind != preparedPatternPaint {
		return nil
	}
	if .pattern == nil {
		return fmt.Errorf("d2raster: internal nil prepared pattern")
	}
	if .pattern.tileResource == nil {
		return fmt.Errorf("d2raster: internal nil prepared pattern tile")
	}
	return .pattern.tileResource.render(, )
}

func ( *preparedPatternTile) ( context.Context,  *rasterScratch) error {
	if .image != nil {
		return .Err()
	}
	if .rendering {
		return fmt.Errorf("d2raster: cyclic prepared pattern rendering")
	}
	if  := .Err();  != nil {
		return 
	}
	,  := .offscreen.reserveBytes(.tileBytes, "pattern tile")
	if  != nil {
		return 
	}
	.patternBytes += 
	.rendering = true
	defer func() { .rendering = false }()
	 := image.NewRGBA(.bounds)
	if  := renderNode(, , .root, );  != nil {
		.offscreen.release()
		.patternBytes -= 
		return 
	}
	.image = 
	.patternTiles = append(.patternTiles, )
	return .Err()
}

func ( *preparedPattern) (,  float64) (color.NRGBA, bool) {
	if  == nil || .tileResource == nil || .tileResource.image == nil {
		return color.NRGBA{}, false
	}
	 := .tileResource
	 := .deviceToPattern.Point(d2scene.Point{X: , Y: })
	,  := wrappedPatternCoordinateFromOriginMod(.X, .originModX, .tile.Width)
	if ! {
		return color.NRGBA{}, false
	}
	,  := wrappedPatternCoordinateFromOriginMod(.Y, .originModY, .tile.Height)
	if ! {
		return color.NRGBA{}, false
	}
	 := int(math.Floor( / .tile.Width * float64(.width)))
	 := int(math.Floor( / .tile.Height * float64(.height)))
	 = max(0, min(.width-1, ))
	 = max(0, min(.height-1, ))
	return patternTileColor(, , )
}

func patternTileColor( *preparedPatternTile, ,  int) (color.NRGBA, bool) {
	 := .image.PixOffset(, )
	 := .image.Pix[ : +4]
	 := [3]
	if  == 0 {
		return color.NRGBA{}, false
	}
	if  == 0xff {
		return color.NRGBA{R: [0], G: [1], B: [2], A: }, true
	}
	// RGBA stores premultiplied bytes. This is the same 16-bit conversion used
	// by color.NRGBAModel, written directly to avoid two interface allocations
	// for every sampled pattern pixel.
	return color.NRGBA{
		R: uint8((uint32([0]) * 0xffff / uint32()) >> 8),
		G: uint8((uint32([1]) * 0xffff / uint32()) >> 8),
		B: uint8((uint32([2]) * 0xffff / uint32()) >> 8),
		A: ,
	}, true
}

func wrappedPatternCoordinateFromOriginMod(, ,  float64) (float64, bool) {
	if !finite() || !finite() || !finite() ||  <= 0 {
		return 0, false
	}
	 := math.Mod(, ) - 
	if !finite() {
		return 0, false
	}
	if  >=  {
		 -= 
	} else if  < 0 {
		if  == - {
			return math.Copysign(0, -1), true
		}
		 += 
		if  < 0 {
			 += 
		}
	}
	return , true
}

func ( *rasterScratch) () {
	if  == nil {
		return
	}
	for ,  := range .patternTiles {
		.image = nil
	}
	.patternTiles = nil
	if .patternBytes != 0 {
		.offscreen.release(.patternBytes)
	}
	.patternBytes = 0
}

func collectPreparedPatterns( context.Context,  *preparedNode,  image.Rectangle) ([]*preparedPatternTile, error) {
	 := patternCollector{
		ctx:   ,
		state: make(map[*preparedPatternTile]uint8),
	}
	if  := .node(, );  != nil {
		return nil, 
	}
	return .patterns, nil
}

type patternCollector struct {
	ctx      context.Context
	state    map[*preparedPatternTile]uint8
	patterns []*preparedPatternTile
}

func ( *patternCollector) ( *preparedNode,  image.Rectangle) error {
	if  == nil || .opacity == 0 {
		return nil
	}
	if  := .ctx.Err();  != nil {
		return 
	}
	 := .bounds.Intersect()
	if .Empty() {
		return nil
	}
	 := 
	if len(.filters) != 0 {
		 = .contentBounds
	} else if .opacity < 1 || .blend != d2scene.BlendNormal || .clip != nil || .mask != nil {
		 = 
	}
	if .primitive != nil && !.primitive.bounds.Intersect().Empty() {
		if  := .primitive(.primitive, );  != nil {
			return 
		}
	}
	for ,  := range .children {
		if  := .(, );  != nil {
			return 
		}
	}
	if .mask != nil {
		if  := .(.mask.root, );  != nil {
			return 
		}
	}
	return nil
}

func ( *patternCollector) ( *preparedPrimitive,  image.Rectangle) error {
	if .image != nil {
		return nil
	}
	if .vector != nil {
		return .node(.vector, )
	}
	if .fill != nil && !subpathPixelBounds(.subpaths, .transform, 0, ).Empty() {
		if  := .paint(.fill);  != nil {
			return 
		}
	}
	if .stroke != nil && !paintedStrokePixelBounds(.strokeRuns, .transform, .stroke, ).Empty() {
		if  := .paint(.stroke.paint);  != nil {
			return 
		}
	}
	return nil
}

func ( *patternCollector) ( *preparedPaint) error {
	if  == nil || .kind != preparedPatternPaint {
		return nil
	}
	 := .pattern
	if  == nil || .tileResource == nil {
		return fmt.Errorf("d2raster: internal nil prepared pattern")
	}
	 := .tileResource
	switch .state[] {
	case 1:
		return fmt.Errorf("d2raster: cyclic prepared pattern graph")
	case 2:
		return nil
	}
	.state[] = 1
	if  := .node(.root, .bounds);  != nil {
		return 
	}
	.state[] = 2
	.patterns = append(.patterns, )
	return nil
}