package render

import (
	_ "embed"
	"unsafe"

	"edgaru089.ink/go/gl01/internal/util/itype"
	"github.com/go-gl/gl/all-core/gl"
)

var (
	screenQuadVerts = []float32{
		-1, -1,
		0, 0,
		1, -1,
		1, 0,
		1, 1,
		1, 1,
		-1, -1,
		0, 0,
		1, 1,
		1, 1,
		-1, 1,
		0, 1,
	}
	screenQuadVAO uint32
	screenQuadVBO uint32

	drawTextureShader *Shader // Shader for dumping textures to screen
)

func initScreenQuad() {
	gl.GenBuffers(1, &screenQuadVBO)
	gl.BindBuffer(gl.ARRAY_BUFFER, screenQuadVBO)
	gl.BufferData(gl.ARRAY_BUFFER, int(unsafe.Sizeof(float32(0)))*len(screenQuadVerts), gl.Ptr(screenQuadVerts), gl.STATIC_DRAW)

	gl.GenVertexArrays(1, &screenQuadVAO)
	gl.BindVertexArray(screenQuadVAO)

	gl.VertexAttribPointer(0, 2, gl.FLOAT, false, int32(4*unsafe.Sizeof(float32(0))), gl.PtrOffset(0))
	gl.VertexAttribPointer(1, 2, gl.FLOAT, false, int32(4*unsafe.Sizeof(float32(0))), gl.PtrOffset(int(2*unsafe.Sizeof(float32(0)))))
	gl.EnableVertexAttribArray(0)
	gl.EnableVertexAttribArray(1)
}

// DrawScreenQuad draws a Quad covering the entire screen with the current binding shader.
//
// Attribute: (location=0) vert:     [ -1.0 --> 1.0]
//            (location=1) texCoord: [ 0    --> 1]
func DrawScreenQuad() {
	if screenQuadVAO == 0 {
		initScreenQuad()
	}

	gl.Disable(gl.DEPTH_TEST)
	gl.Disable(gl.CULL_FACE)
	gl.BindVertexArray(screenQuadVAO)
	gl.DrawArrays(gl.TRIANGLES, 0, 6)
}

// DrawTextureChannels specifies the channels in RGBA to be drawn.
type DrawTextureChannels int

const (
	DrawTextureChannels_R DrawTextureChannels = 1 << iota
	DrawTextureChannels_G
	DrawTextureChannels_B
	DrawTextureChannels_A
)

var (
	//go:embed drawtexture.vert
	drawTextureShaderVert string
	//go:embed drawtexture.frag
	drawTextureShaderFrag string
)

func initDrawTexture() {
	var err error
	drawTextureShader, err = NewShader(drawTextureShaderVert, drawTextureShaderFrag)
	if err != nil {
		panic(err)
	}

	gl.BindFragDataLocation(drawTextureShader.Handle(), 0, gl.Str("outputColor\x00"))
}

// DrawTexture dumps the contents of a Texture onto the portion of the screen.
// It disables depth test and cull face.
//
// onScreen is specified in [-1, 1] size.
//
// If only one channel is specified, that channel is drawn in black and white.
// Otherwise, R/G/B are drawn in their specified channels and A is ignored.
//
// The texture values from vauleRange is mapped to [0, 1] linearly.
//
// TODO: This does not work. Maybe something wrong with the shaders or blending
func DrawTexture(texture uint32, onScreen itype.Rectf, channels DrawTextureChannels, valueMin, valueMax float32) {
	if screenQuadVAO == 0 {
		initScreenQuad()
	}
	if drawTextureShader == nil {
		initDrawTexture()
	}

	drawTextureShader.UseProgram()

	drawTextureShader.SetUniformVec2f("onScreenPos", onScreen.MinPoint())
	drawTextureShader.SetUniformVec2f("onScreenSize", onScreen.Size())
	drawTextureShader.SetUniformFloat("valueMin", valueMin)
	drawTextureShader.SetUniformFloat("valueMax", valueMax)
	drawTextureShader.SetUniformInt("channels", int32(channels))
	drawTextureShader.SetUniformTextureHandle("tex", texture)

	drawTextureShader.BindTextures()

	gl.Disable(gl.DEPTH_TEST)
	gl.Disable(gl.CULL_FACE)
	gl.BindVertexArray(screenQuadVAO)
	gl.DrawArrays(gl.TRIANGLES, 0, 6)
}
