module Spear.GLSL.VAO
(
    VAO
    -- * Creation and destruction
,   newVAO
,   releaseVAO
    -- * Manipulation
,   bindVAO
,   enableVAOAttrib
,   attribVAOPointer
    -- * Rendering
,   drawArrays
,   drawElements
)
where


import Spear.Setup
import Control.Monad.Trans.Class (lift)
import Foreign.Marshal.Utils as Foreign (with)
import Foreign.Marshal.Alloc (alloca)
import Foreign.Storable (peek)
import Foreign.Ptr
import Unsafe.Coerce
import Graphics.Rendering.OpenGL.Raw.Core31


-- | Represents a vertex array object.
data VAO = VAO
    { getVAO :: GLuint
    , rkey   :: Resource
    }


instance Eq VAO where
    vao1 == vao2 = getVAO vao1 == getVAO vao2


instance Ord VAO where
    vao1 < vao2 = getVAO vao1 < getVAO vao2


-- | Create a new 'VAO'.
newVAO :: Setup VAO
newVAO = do
    h <- setupIO . alloca $ \ptr -> do
        glGenVertexArrays 1 ptr
        peek ptr
    
    rkey <- register $ deleteVAO h
    return $ VAO h rkey


-- | Release the given 'VAO'.
releaseVAO :: VAO -> Setup ()
releaseVAO = release . rkey


-- | Delete the given 'VAO'.
deleteVAO :: GLuint -> IO ()
deleteVAO vao = Foreign.with vao $ glDeleteVertexArrays 1


-- | Bind the given 'VAO'.
bindVAO :: VAO -> IO ()
bindVAO = glBindVertexArray . getVAO


-- | Enable the given vertex attribute of the bound 'VAO'.
enableVAOAttrib :: GLuint -> IO ()
enableVAOAttrib = glEnableVertexAttribArray


-- | Bind the bound buffer to the given point.
attribVAOPointer :: GLuint -> GLint -> GLenum -> Bool -> GLsizei -> Int -> IO ()
attribVAOPointer idx ncomp dattype normalise stride off =
    glVertexAttribPointer idx ncomp dattype (unsafeCoerce normalise) stride (unsafeCoerce off)


-- | Draw the bound 'VAO'.
drawArrays :: GLenum -> Int -> Int -> IO ()
drawArrays mode first count = glDrawArrays mode (unsafeCoerce first) (unsafeCoerce count)


-- | Draw the bound 'VAO', indexed mode.
drawElements :: GLenum -> Int -> GLenum -> Ptr a -> IO ()
drawElements mode count t idxs = glDrawElements mode (unsafeCoerce count) t idxs