package d2scenebuild

import (
	
	
	
	

	
	
)

const (
	paperPatternAssetID d2scene.AssetID = "builtin:fill-pattern:paper:" + paperPatternSourceSHA256
	grainPatternAssetID d2scene.AssetID = "builtin:fill-pattern:grain:" + grainPatternPNGSourceSHA256
)

type builtinPattern struct {
	name  string
	paint d2scene.PatternPaint
}

var paperPatternColors = [...]color.NRGBA{
	{A: 0xff},
	{R: 0xef, G: 0xef, B: 0xef, A: 0xff},
	{R: 0xf5, G: 0xf5, B: 0xf5, A: 0xff},
	{R: 0xf7, G: 0xf7, B: 0xf7, A: 0xff},
	{R: 0xf9, G: 0xf9, B: 0xf9, A: 0xff},
	{R: 0xfc, G: 0xfc, B: 0xfc, A: 0xff},
	{R: 0xff, G: 0xff, B: 0xff, A: 0xff},
}

func canonicalFillPattern( string) (string, error) {
	switch  := strings.ToLower();  {
	case "", "none", "dots", "lines", "grain", "paper":
		return , nil
	default:
		return "", fmt.Errorf("must be one of none, dots, lines, grain, or paper")
	}
}

func validateFillPattern(,  string) error {
	if ,  := canonicalFillPattern();  != nil {
		return invalidField(, "fillPattern", , .Error())
	}
	return nil
}

func ( *builder) ( string) (*builtinPattern, error) {
	,  := canonicalFillPattern()
	if  != nil {
		return nil, 
	}
	switch  {
	case "", "none":
		return nil, nil
	case "dots":
		 := d2scene.NewNode(nil)
		.ID = "builtin:fill-pattern:dots:tile"
		.Opacity = .1
		.Blend = d2scene.BlendMultiply
		 := d2scene.SolidPaint{Color: color.NRGBA{R: 0x0a, G: 0x0f, B: 0x25, A: 0xff}}
		for ,  := range [][2]float64{
			{2, 2}, {12, 2}, {12, 12}, {2, 12}, {2, 7}, {12, 7}, {7, 2}, {7, 12}, {7, 7},
		} {
			 := d2scene.NewNode(d2scene.Rect{
				Box: d2scene.Box{X: [0], Y: [1], Width: 1, Height: 1}, Fill: ,
			})
			.ID = fmt.Sprintf("builtin:fill-pattern:dots:%.0f:%.0f", [0], [1])
			.Children = append(.Children, )
		}
		return newBuiltinPattern(, d2scene.Box{Width: 15, Height: 15}, ), nil
	case "lines":
		 := d2scene.NewNode(nil)
		.ID = "builtin:fill-pattern:lines:tile"
		.Opacity = .05
		.Blend = d2scene.BlendMultiply
		 := d2scene.SolidPaint{Color: color.NRGBA{R: 0x0a, G: 0x0f, B: 0x25, A: 0xff}}
		for ,  := range []float64{2, 7, 12} {
			 := d2scene.NewNode(d2scene.Rect{
				Box: d2scene.Box{Y: , Width: 15, Height: 1}, Fill: ,
			})
			.ID = fmt.Sprintf("builtin:fill-pattern:lines:%.0f", )
			.Children = append(.Children, )
		}
		return newBuiltinPattern(, d2scene.Box{Width: 15, Height: 15}, ), nil
	case "grain":
		if  := .ensureGrainPatternAsset();  != nil {
			return nil, 
		}
		 := d2scene.NewNode(nil)
		.ID = "builtin:fill-pattern:grain:tile"
		.Opacity = .8
		 := d2scene.NewNode(d2scene.Image{
			Asset: grainPatternAssetID,
			Box:   d2scene.Box{Width: 300, Height: 300},
			Aspect: d2scene.AspectRatio{
				Align: d2scene.AlignNone,
			},
		})
		.ID = "builtin:fill-pattern:grain:image"
		.Opacity = .9
		.Children = []*d2scene.Node{}
		return newBuiltinPattern(, d2scene.Box{Width: 300, Height: 300}, ), nil
	case "paper":
		if  := .ensurePaperPatternAsset();  != nil {
			return nil, 
		}
		 := d2scene.NewNode(d2scene.Image{
			Asset: paperPatternAssetID,
			Box:   d2scene.Box{Width: 75, Height: 75},
			Aspect: d2scene.AspectRatio{
				Align: d2scene.AlignNone,
			},
		})
		.ID = "builtin:fill-pattern:paper:image"
		return newBuiltinPattern(, d2scene.Box{Width: 75, Height: 75}, ), nil
	default:
		panic("unreachable fill pattern")
	}
}

func newBuiltinPattern( string,  d2scene.Box,  *d2scene.Node) *builtinPattern {
	return &builtinPattern{
		name: ,
		paint: d2scene.PatternPaint{
			Tile: , Root: , Units: d2scene.UserSpaceOnUse, Transform: d2scene.Identity(),
		},
	}
}

func ( *builder) () error {
	if ,  := .assets[grainPatternAssetID];  {
		return nil
	}
	,  := sharedGrainPatternSource(.ctx)
	if  != nil {
		return fmt.Errorf("scene: load built-in grain fill pattern: %w", )
	}
	.assets[grainPatternAssetID] = d2scene.RasterAsset{
		MIMEType: "image/png", Data: append([]byte(nil), .png...),
		PixelWidth: .pixelWidth, PixelHeight: .pixelHeight,
		DecodedBytes: .decodedBytes,
	}
	return nil
}

func ( *builder) () error {
	if ,  := .assets[paperPatternAssetID];  {
		return nil
	}
	,  := sharedPaperPatternSource(.ctx)
	if  != nil {
		return fmt.Errorf("scene: load built-in paper fill pattern: %w", )
	}
	,  := newPaperPatternAsset(.ctx, )
	if  != nil {
		return fmt.Errorf("scene: build built-in paper fill pattern: %w", )
	}
	.assets[paperPatternAssetID] = 
	return nil
}

func newPaperPatternAsset( context.Context,  *paperPatternSource) (d2scene.VectorAsset, error) {
	if  == nil {
		 = context.Background()
	}
	if  == nil || len(.paths) != paperPatternPathCount {
		return d2scene.VectorAsset{}, fmt.Errorf("invalid shared paper pattern source")
	}
	 := d2scene.NewNode(nil)
	.ID = "builtin:fill-pattern:paper:content"
	.Opacity = .3
	.Blend = d2scene.BlendMultiply
	.Children = make([]*d2scene.Node, len(.paths))
	 := 0
	for ,  := range .paths {
		if &255 == 0 {
			if  := .Err();  != nil {
				return d2scene.VectorAsset{}, 
			}
		}
		if int(.color) >= len(paperPatternColors) || len(.commands) == 0 {
			return d2scene.VectorAsset{}, fmt.Errorf("paper path %d has invalid cached data", )
		}
		 := append([]d2scene.PathCommand(nil), .commands...)
		 := d2scene.NewNode(d2scene.Path{
			Commands: ,
			Fill:     d2scene.SolidPaint{Color: paperPatternColors[.color]},
		})
		.ID = fmt.Sprintf("builtin:fill-pattern:paper:path:%d", )
		.Children[] = 
		 += len()
	}
	if  != paperPatternCommandCount {
		return d2scene.VectorAsset{}, fmt.Errorf("paper command count %d, want %d", , paperPatternCommandCount)
	}
	return d2scene.VectorAsset{ViewBox: d2scene.Box{Width: 75, Height: 75}, Root: }, nil
}

func overlayPatternNode( *d2scene.Node,  *builtinPattern,  string) (*d2scene.Node, error) {
	if  == nil ||  == nil {
		return nil, fmt.Errorf("scene: cannot pattern a nil node or pattern")
	}
	if len(.Children) != 0 {
		return nil, fmt.Errorf("scene: cannot apply fill pattern to node %q with children", .ID)
	}
	if len(.Animations) != 0 {
		return nil, fmt.Errorf("scene: cannot apply fill pattern to animated node %q", .ID)
	}
	var  d2scene.Primitive
	switch value := .Primitive.(type) {
	case d2scene.Rect:
		.Fill = .paint
		.Stroke = nil
		 = 
	case *d2scene.Rect:
		if  == nil {
			return nil, fmt.Errorf("scene: node %q has a nil rectangle", .ID)
		}
		 := *
		.Fill = .paint
		.Stroke = nil
		 = 
	case d2scene.Ellipse:
		.Fill = .paint
		.Stroke = nil
		 = 
	case *d2scene.Ellipse:
		if  == nil {
			return nil, fmt.Errorf("scene: node %q has a nil ellipse", .ID)
		}
		 := *
		.Fill = .paint
		.Stroke = nil
		 = 
	case d2scene.Path:
		.Fill = .paint
		.Stroke = nil
		 = 
	case *d2scene.Path:
		if  == nil {
			return nil, fmt.Errorf("scene: node %q has a nil path", .ID)
		}
		 := *
		.Fill = .paint
		.Stroke = nil
		 = 
	default:
		return nil, fmt.Errorf("scene: node %q primitive %T cannot carry a fill pattern", .ID, .Primitive)
	}
	 := d2scene.NewNode()
	.ID = 
	if .ID != "" {
		.ID = .ID + ":fill-pattern"
	}
	.Classes = []string{.name + "-overlay"}
	.Transform = .Transform
	.Opacity = .Opacity
	.Blend = d2scene.BlendMultiply
	.Clip = .Clip
	.Mask = .Mask
	.Filters = append([]d2scene.Filter(nil), .Filters...)
	return , nil
}

func ( *builder) ( []*d2scene.Node,  *builtinPattern,  string,  func(*d2scene.Node) bool) ([]*d2scene.Node, error) {
	if  == nil {
		return , nil
	}
	 := make([]*d2scene.Node, 0, len()*2)
	for ,  := range  {
		if  := .ctx.Err();  != nil {
			return nil, 
		}
		 = append(, )
		if  != nil && !() {
			continue
		}
		,  := overlayPatternNode(, , fmt.Sprintf("%s:fill-pattern:%d", , ))
		if  != nil {
			return nil, 
		}
		 = append(, )
	}
	return , nil
}

func ordinaryPatternNode(*d2scene.Node) bool { return true }

func structuredPatternNode( string) func(*d2scene.Node) bool {
	return func( *d2scene.Node) bool {
		return  != nil && (.ID == +":outline" ||
			.ID == +":class-header" || .ID == +":table-header")
	}
}

func effectPatternNode( d2target.Shape) func(*d2scene.Node) bool {
	return func( *d2scene.Node) bool {
		if  == nil {
			return false
		}
		if .ThreeDee {
			return .ID == .ID+":3d-main"
		}
		if .DoubleBorder {
			if .ID == .ID+":double-border:outer" {
				return true
			}
			if .Multiple && .ID == .ID+":multiple:outer" {
				switch .Type {
				case "", d2target.ShapeRectangle, d2target.ShapeSquare:
					return true
				}
			}
			return false
		}
		if .Multiple {
			return .ID == .ID+":main" || strings.HasPrefix(.ID, .ID+":main:path:")
		}
		return false
	}
}