package render

import (
	"image"
	"image/color"
	"math"

	"golang.org/x/image/draw"
)

// Pattern provides infinite algorithmically generated Patterns and implements
// the image.Image interface.
type Pattern struct {
}

// Image interface
func (c *Pattern) ColorModel() color.Model { return color.NRGBAModel }
func (c *Pattern) At(x, y int) color.Color { return color.Transparent }
func (c *Pattern) Bounds() image.Rectangle {
	return image.Rectangle{image.Point{-1e9, -1e9}, image.Point{1e9, 1e9}}
}

func (p *Pattern) ToFixedImage(bounds image.Rectangle) *image.NRGBA {
	img := image.NewNRGBA(bounds)
	// TODO: allow setting a mask?
	// override size for pattern?
	draw.Draw(img, bounds, p, image.Point{}, draw.Src)
	return img
}

type StripePattern struct {
	Pattern
	Size    int
	Palette color.Palette
}

func (c *StripePattern) At(x, y int) color.Color {
	n := len(c.Palette)
	if n == 0 {
		return color.Transparent
	}

	pxPerStripe := c.Size / n
	if pxPerStripe <= 0 {
		pxPerStripe = 1
	}
	stripeIndex := y / pxPerStripe % n // FIXME: y - c.Size? -> negative modulo fix
	return c.Palette[stripeIndex]
}

func NewPrideImage(p color.Palette, bounds image.Rectangle) image.Image {
	Pattern := StripePattern{Palette: p}
	return Pattern.ToFixedImage(bounds)
}

var PrideFlags = map[string]color.Palette{
	"lgbti": color.Palette{
		color.NRGBA{0xe4, 0x03, 0x03, 0xff},
		color.NRGBA{0xff, 0x8c, 0x00, 0xff},
		color.NRGBA{0xff, 0xed, 0x00, 0xff},
		color.NRGBA{0x00, 0x80, 0x26, 0xff},
		color.NRGBA{0x00, 0x4d, 0xff, 0xff},
		color.NRGBA{0x75, 0x07, 0x87, 0xff},
	},
	"nonbinary": color.Palette{
		color.NRGBA{0x9c, 0x5c, 0xd4, 0xff},
		color.NRGBA{0xfc, 0xfc, 0xfc, 0xff},
		color.NRGBA{0xfc, 0xf4, 0x34, 0xff},
		color.NRGBA{0x2c, 0x2c, 0x2c, 0xff},
	},
	"trans": color.Palette{
		color.NRGBA{0x5b, 0xde, 0xfa, 0xff},
		color.NRGBA{0xf5, 0xa9, 0xb8, 0xff},
		color.NRGBA{0xff, 0xff, 0xff, 0xff},
		color.NRGBA{0xf5, 0xa9, 0xb8, 0xff},
		color.NRGBA{0x5b, 0xde, 0xfa, 0xff},
	},
}

type DynamicPattern struct {
	Pattern

	Color func(x, y int, p *DynamicPattern) (float64, float64, float64) // should return rgb in [0,1] range

	Luma  float64 // color parameter
	Freq  float64 // frequency parameter
	Phase float64 // phase parameter

	Brightness float64 // additive base brightness
	ColFactor  float64 // Pattern intensity
}

func (c *DynamicPattern) At(x, y int) color.Color {
	col := color.NRGBA{}

	if c.ColFactor == 0.0 && c.Brightness == 0.0 {
		c.ColFactor = 1.0
	}
	if c.Color != nil {
		r, g, b := c.Color(x, y, c)
		col.R = uint8(r * c.ColFactor * 255)
		col.G = uint8(g * c.ColFactor * 255)
		col.B = uint8(b * c.ColFactor * 255)
	}
	if c.Freq == 0.0 {
		c.Freq = 1.0
	}

	col.R = col.R + uint8(255*c.Brightness)
	col.G = col.G + uint8(255*c.Brightness)
	col.B = col.B + uint8(255*c.Brightness)
	col.A = 0xff

	return col
}

var DynPatterns = map[string]image.Image{
	"rbow": &DynamicPattern{
		Color: func(x, y int, p *DynamicPattern) (float64, float64, float64) {
			xx := float64(x) / 26.0 // TODO: dynamic sizing
			yy := float64(y) / 13.0
			r := 0.5 + 0.5*math.Cos(xx+p.Phase)
			g := 0.5 + 0.5*math.Sin(yy+p.Phase)
			b := 0.5 + 0.5*math.Sin(xx+p.Phase)*math.Cos(yy+p.Phase)
			return r, g, b
		},
	},

	"pastel": &DynamicPattern{
		Brightness: 0.5, // TODO: expose these params?
		ColFactor:  0.5,
		Freq:       5.0,

		Color: func(x, y int, p *DynamicPattern) (float64, float64, float64) {
			offset := 0.0
			v := math.Atan2(float64(x)-offset, float64(y)-offset) + p.Phase
			r := 0.5 + 0.5*math.Sin(v*p.Freq)
			g := 0.5 + 0.5*math.Cos(v*p.Freq)
			b := 0.5 + 0.5*math.Sin(v*p.Freq+6.28318/p.Luma)
			return r, g, b
		},
	},
}
