module Graphics.Rendering.Ombra.Image (
        Image,
        image,
        uimage,
        draw
) where

import Data.Foldable
import Data.Functor.Identity
import Data.Proxy

import Graphics.Rendering.Ombra.Internal.GL (gl)
import Graphics.Rendering.Ombra.Draw.Class
import Graphics.Rendering.Ombra.Geometry
import Graphics.Rendering.Ombra.Geometry.Draw
import Graphics.Rendering.Ombra.Shader
import Graphics.Rendering.Ombra.Shader.CPU
import Graphics.Rendering.Ombra.Shader.GLSL
import Graphics.Rendering.Ombra.Shader.Program
import Graphics.Rendering.Ombra.Shader.Types
import Graphics.Rendering.Ombra.Image.Types
import Graphics.Rendering.Ombra.Texture.Draw

-- | Create an 'Image'.
image :: (ShaderInput i, GeometryVertex i, ElementType e, ShaderInput v)
      => VertexShader i (GVec4, v)
      -> FragmentShader v o
      -> Geometry e i
      -> Image o
image vs fs g = uimage (const vs) (const fs) (Identity (g, (), ()))

-- | Create an 'Image' using the same shader with different uniforms for each
-- geometry. The resulting image can be rendered more efficiently than an
-- equivalent sequence of images created with 'image'.
uimage :: ( ShaderInput i
          , GeometryVertex i
          , ElementType e
          , ShaderInput v
          , Foldable t
          )
       => (UniformSetter vu -> VertexShader i (GVec4, v))
       -> (UniformSetter fu -> FragmentShader v o)
       -> t (Geometry e i, vu, fu)
       -> Image o
uimage vs fs g = Image g vs fs

-- | Draw an 'Image'.
draw :: (MonadDraw o m, FragmentShaderOutput o) => Image o -> m o ()
draw (Image geometries vs fs) =
        let prg = program (vs undefined) (fs undefined)
            -- XXX
        in do setProgram prg
              for_ geometries $ \(geometry, vu, fu) ->
                let (uid, unisv, texsv) = uniformList (vs $ return vu) 0
                    (_, unisf, texsf) = uniformList (fs $ return fu) uid
                    unis = unisv ++ unisf
                    texs = texsv ++ texsf
                in withActiveTextures texs (const $ return ()) $ \samplers ->
                        do setUniforms unis $ zip texs samplers
                           drawGeometry geometry
                     
        where setUniforms unis texs =
                for_ unis $ \(uid, uniformValue) ->
                        case uniformValue of
                             UniformValue proxy value ->
                                setUniform uid proxy value
                             UniformTexture tex ->
                                let proxy = Proxy :: Proxy TextureSampler
                                    Just value = lookup tex texs
                                in setUniform uid proxy value

draw (SeqImage i i') = draw i >> draw i'
draw NoImage = return ()