package render

import (
	"image"

	"github.com/go-gl/gl/all-core/gl"
)

func curTextureBinding() uint32 {
	var id int32
	gl.GetIntegerv(gl.TEXTURE_BINDING_2D, &id)
	return uint32(id)
}

// Texture holds handle to OpenGL Texture on the graphics card memory.
type Texture struct {
	tex uint32

	hasMipmap bool
}

// NewTexture creates a new, empty Texture.
func NewTexture() *Texture {
	// Restore current texture binding
	defer gl.BindTexture(gl.TEXTURE_2D, curTextureBinding())

	var tex uint32

	gl.GenTextures(1, &tex)
	gl.BindTexture(gl.TEXTURE_2D, tex)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)

	return &Texture{tex: tex}
}

// NewTextureRGBA creates a new Texture with image.
func NewTextureRGBA(image *image.RGBA) *Texture {
	// Restore current texture binding
	defer gl.BindTexture(gl.TEXTURE_2D, curTextureBinding())

	var tex uint32

	gl.GenTextures(1, &tex)
	gl.BindTexture(gl.TEXTURE_2D, tex)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)

	gl.TexImage2D(
		gl.TEXTURE_2D,
		0,
		gl.RGBA,
		int32(image.Rect.Size().X),
		int32(image.Rect.Size().Y),
		0,
		gl.RGBA,
		gl.UNSIGNED_BYTE,
		gl.Ptr(image.Pix),
	)

	return &Texture{tex: tex}
}

// SetSmooth sets the min/mag filters to LINEAR(smooth) or NEAREST(not smooth)
// TODO: Not working
func (t *Texture) SetSmooth(smooth bool) {
	defer gl.BindTexture(gl.TEXTURE_2D, curTextureBinding())
	gl.BindTexture(gl.TEXTURE_2D, t.tex)
	if smooth {
		gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
		gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
	} else {
		gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
		gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
	}
}

// UpdateRGBA updates the content of the texture with image.
// It deletes existing mipmap, you need to generate it again.
func (t *Texture) UpdateRGBA(image *image.RGBA) {

	// Restore current texture binding
	defer gl.BindTexture(gl.TEXTURE_2D, curTextureBinding())

	gl.BindTexture(gl.TEXTURE_2D, t.tex)
	gl.TexImage2D(
		gl.TEXTURE_2D,
		0,
		gl.RGBA,
		int32(image.Rect.Size().X),
		int32(image.Rect.Size().Y),
		0,
		gl.RGBA,
		gl.UNSIGNED_BYTE,
		gl.Ptr(image.Pix),
	)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)

	t.hasMipmap = false

}

// GenerateMipMap generates mipmap for the texture.
func (t *Texture) GenerateMipMap() {

	// Restore current texture binding
	defer gl.BindTexture(gl.TEXTURE_2D, curTextureBinding())

	gl.BindTexture(gl.TEXTURE_2D, t.tex)
	gl.GenerateMipmap(gl.TEXTURE_2D)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_LINEAR)

	t.hasMipmap = true

}

// InvalidateMipMap invalidates mipmap for the texture.
func (t *Texture) InvalidateMipMap() {

	// Restore current texture binding
	defer gl.BindTexture(gl.TEXTURE_2D, curTextureBinding())

	gl.BindTexture(gl.TEXTURE_2D, t.tex)
	gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)

	t.hasMipmap = false
}

// Handle returns the OpenGL handle of the texture.
func (t *Texture) Handle() uint32 {
	return t.tex
}

// Free deletes the texture.
func (t *Texture) Free() {
	if t.tex != 0 {
		gl.DeleteTextures(1, &t.tex)
	}
}
