package main

import (
	"fmt"
	"os"
	"runtime"

	"git.tek.govt.hu/dowerx/opengl-deferred/types/geometry"
	"git.tek.govt.hu/dowerx/opengl-deferred/types/shader"
	"git.tek.govt.hu/dowerx/opengl-deferred/types/texture"
	"github.com/go-gl/gl/v4.6-core/gl"
	"github.com/go-gl/glfw/v3.3/glfw"
	"github.com/go-gl/mathgl/mgl32"
)

const (
	WIDTH  = 640
	HEIGHT = 480
	TITLE  = "opengl-deferred"
	FOV    = 45.0
)

func main() {

	// init glfw
	runtime.LockOSThread()
	if err := glfw.Init(); err != nil {
		panic(err)
	}
	defer glfw.Terminate()

	glfw.WindowHint(glfw.Resizable, glfw.False)
	glfw.WindowHint(glfw.ContextVersionMajor, 4)
	glfw.WindowHint(glfw.ContextVersionMinor, 6)
	glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
	glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)

	window, err := glfw.CreateWindow(WIDTH, HEIGHT, TITLE, nil, nil)
	if err != nil {
		panic(err)
	}
	window.MakeContextCurrent()

	// init gl
	if err := gl.Init(); err != nil {
		panic(err)
	}

	gl.Enable(gl.DEPTH_TEST)
	gl.Enable(gl.CULL_FACE)
	gl.DepthFunc(gl.LESS)
	gl.ClearColor(0, 0, 0, 0)

	fmt.Println("OpenGL version:", gl.GoStr(gl.GetString(gl.VERSION)))

	// load assets
	// shader
	vertexSource, err := os.ReadFile("assets/shader/deferred/deferred.vs")
	if err != nil {
		panic(err)
	}
	fragmentSource, err := os.ReadFile("assets/shader/deferred/deferred.fs")
	if err != nil {
		panic(err)
	}

	deferredShader, err := shader.New(string(vertexSource), string(fragmentSource))
	if err != nil {
		panic(err)
	}
	defer deferredShader.Delete()

	// geometry
	geometries, err := geometry.LoadOBJ("assets/geometries/suzanne.obj")
	if err != nil {
		panic(err)
	}
	defer func() {
		for _, geometry := range geometries {
			geometry.Delete()
		}
	}()

	// textures
	albedo, err := texture.Load("assets/textures/demo.png")
	if err != nil {
		panic(err)
	}
	defer albedo.Delete()

	normal, err := texture.Load("assets/textures/demo.png")
	if err != nil {
		panic(err)
	}
	defer normal.Delete()

	specular, err := texture.Load("assets/textures/demo.png")
	if err != nil {
		panic(err)
	}
	defer specular.Delete()

	roughness, err := texture.Load("assets/textures/demo.png")
	if err != nil {
		panic(err)
	}
	defer roughness.Delete()

	metalic, err := texture.Load("assets/textures/demo.png")
	if err != nil {
		panic(err)
	}
	defer metalic.Delete()

	// transformations
	projection := mgl32.Perspective(mgl32.DegToRad(FOV), float32(WIDTH)/HEIGHT, 0.1, 10)
	camera := mgl32.LookAtV(mgl32.Vec3{3, 3, 3}, mgl32.Vec3{0, 0, 0}, mgl32.Vec3{0, 1, 0})
	model := mgl32.Ident4()

	// set textures
	albedo.Bind(gl.TEXTURE0)
	normal.Bind(gl.TEXTURE1)
	specular.Bind(gl.TEXTURE2)
	roughness.Bind(gl.TEXTURE3)
	metalic.Bind(gl.TEXTURE4)

	// transfer uniforms
	deferredShader.Use()
	deferredShader.Mat4("projection", projection)
	deferredShader.Mat4("camera", camera)
	deferredShader.Mat4("model", model)

	deferredShader.Int("s_albedo", 0)
	deferredShader.Int("s_normal", 1)
	deferredShader.Int("s_specular", 2)
	deferredShader.Int("s_roughness", 3)
	deferredShader.Int("s_metalic", 4)

	for !window.ShouldClose() {
		gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

		// render
		for _, g := range geometries {
			g.Draw()
		}

		window.SwapBuffers()
		glfw.PollEvents()
	}

	defer fmt.Println("exiting...")
}
