package me.eater.threedom.gl.dom

import me.eater.threedom.dom.IDocument
import me.eater.threedom.dom.Node
import me.eater.threedom.gl.ShaderProgram
import me.eater.threedom.gl.vertex.VertexArrayObject
import me.eater.threedom.utils.joml.toFloat
import org.joml.Matrix3d
import org.joml.Matrix4d
import org.lwjgl.opengl.GL30.*

open class TriMesh(document: IDocument?) : Node<TriMesh>(document), GLRenderNode<TriMesh> {
    var mesh: VertexArrayObject? = null
    var program: ShaderProgram? = null
        private set
    var programConfig: (ShaderProgram.(TriMesh) -> Unit)? = null
        private set

    fun <T : ShaderProgram> use(program: T, block: T.(TriMesh) -> Unit) {
        this.program = program
        @kotlin.Suppress("UNCHECKED_CAST")
        this.programConfig = block as ShaderProgram.(TriMesh) -> Unit
    }

    fun <T : ShaderProgram> use(program: T) {
        this.program = program
        this.programConfig = null
    }

    override fun cloneSelf(): TriMesh {
        return TriMesh(document).also {
            it.mesh = mesh
            it.program = program
            it.programConfig = programConfig
        }
    }

    override fun render(renderer: ICamera<*>) {
        val mesh = mesh ?: return
        val program = program ?: return
        program.enable()
        programConfig?.let { program.it(this) }
        program.camera(renderer)
        program.model = absolute.toFloat()
        program.normalModel = model.invert(Matrix4d()).transpose().get3x3(Matrix3d()).toFloat()
        glBindVertexArray(mesh.id)
        glDrawArrays(GL_TRIANGLES, 0, mesh.size)
    }
}
