package xcanvas

import "image"
import "image/color"
import "github.com/jezek/xgbutil/xgraphics"
import "git.tebibyte.media/tomo/tomo/canvas"

// Texture is a read-only image texture that can be quickly written to a canvas.
// It must be closed manually after use.
type Texture struct {
	pix         []uint8
	stride      int
	rect        image.Rectangle
	transparent bool
}

// NewTextureFrom creates a new texture from a source image.
func NewTextureFrom (source image.Image) *Texture {
	bounds := source.Bounds()
	texture := &Texture {
		pix:    make([]uint8, bounds.Dx() * bounds.Dy() * 4),
		stride: bounds.Dx() * 4,
		rect:   bounds.Sub(bounds.Min),
	}

	index := 0
	var point image.Point
	for point.Y = bounds.Min.Y; point.Y < bounds.Max.Y; point.Y ++ {
	for point.X = bounds.Min.X; point.X < bounds.Max.X; point.X ++ {
		r, g, b, a := source.At(point.X, point.Y).RGBA()
		texture.pix[index + 0] = uint8(b >> 8)
		texture.pix[index + 1] = uint8(g >> 8)
		texture.pix[index + 2] = uint8(r >> 8)
		texture.pix[index + 3] = uint8(a >> 8)
		index += 4

		if a != 0xFFFF {
			texture.transparent = true
		}
	}}

	return texture
}

func (this *Texture) BGRAAt (x, y int) xgraphics.BGRA {
	if !(image.Point{ x, y }.In(this.rect)) {
		return xgraphics.BGRA { }
	}
	index := this.PixOffset(x, y)
	return xgraphics.BGRA {
		B: this.pix[index    ],
		G: this.pix[index + 1],
		R: this.pix[index + 2],
		A: this.pix[index + 3],
	}
}

func (this *Texture) At (x, y int) color.Color {
	return this.BGRAAt(x, y)
}

// Bounds returns the bounding rectangle of this texture.
func (this *Texture) Bounds () image.Rectangle {
	return this.rect
}

func (this *Texture) ColorModel () color.Model {
	return xgraphics.BGRAModel
}

// Opaque reports whether or not the texture is fully opaque.
func (this *Texture) Opaque () bool {
	return !this.transparent
}

func (this *Texture) PixOffset (x, y int) int {
	x = wrap(x, this.rect.Min.X, this.rect.Max.X)
	y = wrap(y, this.rect.Min.Y, this.rect.Max.Y)
	return x * 4 + y * this.stride
}

// Close frees the texture from memory.
func (this *Texture) Close () error {
	// i lied we dont actually need to close this, but we will once this
	// texture resides on the x server or in video memory.
	return nil
}

// SubTexture returns a subset of this texture that points to the same data.
func (this *Texture) SubTexture (bounds image.Rectangle) canvas.Texture {
	clipped := *this
	clipped.rect = bounds
	return &clipped
}

// AssertTexture checks if a given canvas.Texture is a texture from this package.
func AssertTexture (unknown canvas.Texture) *Texture {
	if unknown == nil {
		return nil
	}
	if tx, ok := unknown.(*Texture); ok {
		return tx
	} else {
		panic("foregin texture implementation, i did not make this!")
	}
}