package me.eater.threedom.gl.vertex

import me.eater.threedom.gl.GL.stack
import org.lwjgl.opengl.GL30.*
import kotlin.math.min
import kotlin.reflect.KProperty1

class VertexArrayObject() {
    var size: Int = 0

    val id = run {
        val pId = stack.mallocInt(1)
        glGenVertexArrays(pId)
        pId.get(0)
    }

    constructor(block: VertexArrayObject.() -> Unit) : this() {
        block(this)
    }

    var pointerIndex: Int = 0

    operator fun <T : VertexData> VertexBuffer<T>.invoke(prop: KProperty1<T, *>) =
        bind(this, prop)

    fun <T : VertexData> bind(vertexBuffer: VertexBuffer<T>, prop: KProperty1<T, *>) {
        if (vertexBuffer.items.isEmpty()) {
            return
        }

        size = if (pointerIndex == 0) {
            vertexBuffer.items.size
        } else {
            min(size, vertexBuffer.items.size)
        }

        val first = vertexBuffer.items.first()
        val item = first.getDataPoint(prop) ?: return
        var size = item.slots
        var length = item.length
        var repeat = 1

        var i = 4
        while (size > 4) {
            if ((size % i) == 0) {
                repeat = size / i
                size = i
                length /= i
                break
            }

            i--
        }

        val stride = if (vertexBuffer.interleaved) {
            vertexBuffer.stride
        } else {
            item.length
        }

        var offset = first.getOffset(prop)!!

        if (!vertexBuffer.interleaved) {
            offset *= vertexBuffer.items.size
        }

        glBindVertexArray(id)
        glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.id)
        for (x in 0 until repeat) {
            val pointer = pointerIndex++
            glVertexAttribPointer(pointer, size, item.type, false, stride, offset + (x * length).toLong())
            glEnableVertexAttribArray(pointer)
        }

        glBindBuffer(GL_ARRAY_BUFFER, 0)
        glBindVertexArray(0)
    }
}
