{-# LANGUAGE TypeFamilies, MultiParamTypeClasses,
             FunctionalDependencies, FlexibleInstances #-}

-- FWGL.Shader.Variables? (+ loadUniform, loadAttribute, inputName, etc.)
module FWGL.Shader.CPU (UniformCPU(..), AttributeCPU(..), toGPUBool) where

import qualified Data.Int as CPU
import Data.Word (Word)
import Data.Typeable
import qualified FWGL.Shader.Language.Types as GPU
import FWGL.Internal.GL as CPU
import qualified Data.Vect.Float as CPU
import Prelude as CPU

-- | CPU types convertible to GPU types (as uniforms).
class Typeable g => UniformCPU c g | g -> c where
        setUniform :: UniformLocation -> g -> c -> GL ()

-- | CPU types convertible to GPU types (as attributes).
class Typeable g => AttributeCPU c g | g -> c where
        encodeAttribute :: g -> [c] -> GL Array
        setAttribute :: g -> GLUInt -> GL ()

-- Float

instance GLES => UniformCPU CPU.Float GPU.Float where
        setUniform l _ v = uniform1f l v

instance (Typeable n, GLES) =>
         UniformCPU [CPU.Float] (GPU.Array n GPU.Float) where
        setUniform l _ v = liftIO (encodeFloats v) >>= uniform1fv l

instance GLES => AttributeCPU CPU.Float GPU.Float where
        encodeAttribute _ a = liftIO . fmap fromFloat32Array $ encodeFloats a
        setAttribute _ i = attr gl_FLOAT i 1

-- Bool

instance GLES => UniformCPU CPU.Int32 GPU.Bool where
        setUniform l _ v = uniform1i l v

instance (Typeable n, GLES) =>
         UniformCPU [CPU.Int32] (GPU.Array n GPU.Bool) where
        setUniform l _ v = liftIO (encodeInts v) >>= uniform1iv l

instance GLES => AttributeCPU CPU.Int32 GPU.Bool where
        encodeAttribute _ a = liftIO . fmap fromInt32Array $ encodeInts a
        setAttribute _ i = attr gl_INT i 1

-- Int

instance GLES => UniformCPU CPU.Int32 GPU.Int where
        setUniform l _ v = uniform1i l v

instance (Typeable n, GLES) =>
         UniformCPU [CPU.Int32] (GPU.Array n GPU.Int) where
        setUniform l _ v = liftIO (encodeInts v) >>= uniform1iv l

instance GLES => AttributeCPU CPU.Int32 GPU.Int where
        encodeAttribute _ a = liftIO . fmap fromInt32Array $ encodeInts a
        setAttribute _ i = attr gl_INT i 1

-- TODO: sampler arrays (they're problematic to safely access in the shaders)
-- Samplers

instance GLES => UniformCPU CPU.ActiveTexture GPU.Sampler2D where
        setUniform l _ (CPU.ActiveTexture v) = uniform1i l $ fromIntegral v

instance GLES => UniformCPU CPU.ActiveTexture GPU.SamplerCube where
        setUniform l _ (CPU.ActiveTexture v) = uniform1i l $ fromIntegral v

-- Vec2

instance GLES => UniformCPU CPU.Vec2 GPU.Vec2 where
        setUniform l _ (CPU.Vec2 x y) = uniform2f l x y

instance (Typeable n, GLES) =>
         UniformCPU [CPU.Vec2] (GPU.Array n GPU.Vec2) where
        setUniform l _ v = liftIO (encodeVec2s v) >>= uniform2fv l

instance GLES => AttributeCPU CPU.Vec2 GPU.Vec2 where
        encodeAttribute _ a = liftIO . fmap fromFloat32Array $ encodeVec2s a
        setAttribute _ i = attr gl_FLOAT i 2

-- Vec3

instance GLES => UniformCPU CPU.Vec3 GPU.Vec3 where
        setUniform l _ (CPU.Vec3 x y z) = uniform3f l x y z

instance (Typeable n, GLES) =>
         UniformCPU [CPU.Vec3] (GPU.Array n GPU.Vec3) where
        setUniform l _ v = liftIO (encodeVec3s v) >>= uniform3fv l

instance GLES => AttributeCPU CPU.Vec3 GPU.Vec3 where
        encodeAttribute _ a = liftIO . fmap fromFloat32Array $ encodeVec3s a
        setAttribute _ i = attr gl_FLOAT i 3

-- Vec4

instance GLES => UniformCPU CPU.Vec4 GPU.Vec4 where
        setUniform l _ (CPU.Vec4 x y z w) = uniform4f l x y z w

instance (Typeable n, GLES) =>
         UniformCPU [CPU.Vec4] (GPU.Array n GPU.Vec4) where
        setUniform l _ v = liftIO (encodeVec4s v) >>= uniform4fv l

instance GLES => AttributeCPU CPU.Vec4 GPU.Vec4 where
        encodeAttribute _ a = liftIO . fmap fromFloat32Array $ encodeVec4s a
        setAttribute _ i = attr gl_FLOAT i 4

-- IVec2

instance GLES => UniformCPU CPU.IVec2 GPU.IVec2 where
        setUniform l _ (CPU.IVec2 x y) = uniform2i l x y

instance (Typeable n, GLES) =>
         UniformCPU [CPU.IVec2] (GPU.Array n GPU.IVec2) where
        setUniform l _ v = liftIO (encodeIVec2s v) >>= uniform2iv l

instance GLES => AttributeCPU CPU.IVec2 GPU.IVec2 where
        encodeAttribute _ a = liftIO . fmap fromInt32Array $ encodeIVec2s a
        setAttribute _ i = attr gl_INT i 2

-- IVec3

instance GLES => UniformCPU CPU.IVec3 GPU.IVec3 where
        setUniform l _ (CPU.IVec3 x y z) = uniform3i l x y z

instance (Typeable n, GLES) =>
         UniformCPU [CPU.IVec3] (GPU.Array n GPU.IVec3) where
        setUniform l _ v = liftIO (encodeIVec3s v) >>= uniform3iv l

instance GLES => AttributeCPU CPU.IVec3 GPU.IVec3 where
        encodeAttribute _ a = liftIO . fmap fromInt32Array $ encodeIVec3s a
        setAttribute _ i = attr gl_INT i 3

-- IVec4

instance GLES => UniformCPU CPU.IVec4 GPU.IVec4 where
        setUniform l _ (CPU.IVec4 x y z w) = uniform4i l x y z w

instance (Typeable n, GLES) =>
         UniformCPU [CPU.IVec4] (GPU.Array n GPU.IVec4) where
        setUniform l _ v = liftIO (encodeIVec4s v) >>= uniform4iv l

instance GLES => AttributeCPU CPU.IVec4 GPU.IVec4 where
        encodeAttribute _ a = liftIO . fmap fromInt32Array $ encodeIVec4s a
        setAttribute _ i = attr gl_INT i 4

-- BVec2

instance GLES => UniformCPU CPU.IVec2 GPU.BVec2 where
        setUniform l _ (CPU.IVec2 x y) = uniform2i l x y

instance (Typeable n, GLES) =>
         UniformCPU [CPU.IVec2] (GPU.Array n GPU.BVec2) where
        setUniform l _ v = liftIO (encodeIVec2s v) >>= uniform2iv l

instance GLES => AttributeCPU CPU.IVec2 GPU.BVec2 where
        encodeAttribute _ a = liftIO . fmap fromInt32Array $ encodeIVec2s a
        setAttribute _ i = attr gl_INT i 2

-- BVec3

instance GLES => UniformCPU CPU.IVec3 GPU.BVec3 where
        setUniform l _ (CPU.IVec3 x y z) = uniform3i l x y z

instance (Typeable n, GLES) =>
         UniformCPU [CPU.IVec3] (GPU.Array n GPU.BVec3) where
        setUniform l _ v = liftIO (encodeIVec3s v) >>= uniform3iv l

instance GLES => AttributeCPU CPU.IVec3 GPU.BVec3 where
        encodeAttribute _ a = liftIO . fmap fromInt32Array $ encodeIVec3s a
        setAttribute _ i = attr gl_INT i 3

-- BVec4

instance GLES => UniformCPU CPU.IVec4 GPU.BVec4 where
        setUniform l _ (CPU.IVec4 x y z w) = uniform4i l x y z w

instance (Typeable n, GLES) =>
         UniformCPU [CPU.IVec4] (GPU.Array n GPU.BVec4) where
        setUniform l _ v = liftIO (encodeIVec4s v) >>= uniform4iv l

instance GLES => AttributeCPU CPU.IVec4 GPU.BVec4 where
        encodeAttribute _ a = liftIO . fmap fromInt32Array $ encodeIVec4s a
        setAttribute _ i = attr gl_INT i 4

-- Matrices

instance GLES => UniformCPU CPU.Mat2 GPU.Mat2 where
        setUniform l _ m = liftIO (encodeMat2 m) >>= uniformMatrix2fv l false

instance GLES => UniformCPU CPU.Mat3 GPU.Mat3 where
        setUniform l _ m = liftIO (encodeMat3 m) >>= uniformMatrix3fv l false

instance GLES => UniformCPU CPU.Mat4 GPU.Mat4 where
        setUniform l _ m = liftIO (encodeMat4 m) >>= uniformMatrix4fv l false

attr :: GLES => GLEnum -> GLUInt -> GLInt -> GL ()
attr t i s = vertexAttribPointer i s t false 0 nullGLPtr

toGPUBool :: CPU.Bool -> CPU.Int32
toGPUBool True = 1
toGPUBool False = 0