package pixelgl

import (
	"github.com/go-gl/gl/v3.3-core/gl"
	"github.com/pkg/errors"
)

// Texture is an OpenGL texture.
type Texture struct {
	parent Doer
	tex    uint32
}

// NewTexture creates a new texture with the specified width and height.
// The pixels must be a sequence of RGBA values.
func NewTexture(parent Doer, width, height int, pixels []uint8) (*Texture, error) {
	texture := &Texture{parent: parent}

	var err error
	parent.Do(func(ctx Context) {
		err = DoGLErr(func() {
			gl.GenTextures(1, &texture.tex)
			gl.BindTexture(gl.TEXTURE_2D, texture.tex)

			gl.TexImage2D(
				gl.TEXTURE_2D,
				0,
				gl.RGBA,
				int32(width),
				int32(height),
				0,
				gl.RGBA,
				gl.UNSIGNED_BYTE,
				gl.Ptr(pixels),
			)

			gl.GenerateMipmap(gl.TEXTURE_2D)

			gl.BindTexture(gl.TEXTURE_2D, 0)
		})
	})
	if err != nil {
		return nil, errors.Wrap(err, "failed to create a texture")
	}

	return texture, nil
}

// Delete deletes a texture. Don't use a texture after deletion.
func (t *Texture) Delete() {
	t.parent.Do(func(ctx Context) {
		DoNoBlock(func() {
			gl.DeleteTextures(1, &t.tex)
		})
	})
}

// ID returns an OpenGL identifier of a texture.
func (t *Texture) ID() uint32 {
	return t.tex
}

// Do bind a texture, executes sub, and unbinds the texture.
func (t *Texture) Do(sub func(Context)) {
	t.parent.Do(func(ctx Context) {
		DoNoBlock(func() {
			gl.BindTexture(gl.TEXTURE_2D, t.tex)
		})
		sub(ctx)
		DoNoBlock(func() {
			gl.BindTexture(gl.TEXTURE_2D, 0)
		})
	})
}