    --------------------------------------------------------------------------------
    -- |
    -- Module      :  Graphics.Rendering.OpenGL.GL.Texturing.Specification
    -- Copyright   :  (c) Sven Panne 2002-2009
    -- License     :  BSD-style (see the file libraries/OpenGL/LICENSE)
    --
    -- Maintainer  :  sven.panne@aedion.de
    -- Stability   :  stable
    -- Portability :  portable
    --
    -- This module corresponds to section 3.8.1 (Texture Image Specification),
    -- section 3.8.2 (Alternate Texture Image Specification Commands), and section
    -- 3.8.3 (Compressed Texture Images) of the OpenGL 2.1 specs.
    --
    --------------------------------------------------------------------------------

    module Graphics.Rendering.OpenGL.GL.Texturing.Specification (
       -- * Texture-related Data Types
       TextureTarget(..), CubeMapTarget(..), Level, Border,
       TexturePosition1D(..), TexturePosition2D(..), TexturePosition3D(..),
       TextureSize1D(..), TextureSize2D(..), TextureSize3D(..),

       -- * Texture Image Specification
       texImage1D, texImage2D, texImage3D,
       copyTexImage1D, copyTexImage2D,
       texSubImage1D, texSubImage2D, texSubImage3D,
       getTexImage,

       -- * Alternate Texture Image Specification Commands
       copyTexSubImage1D, copyTexSubImage2D, copyTexSubImage3D,

       -- * Compressed Texture Images
       CompressedTextureFormat(..), compressedTextureFormats,
       CompressedPixelData(..),
       compressedTexImage1D, compressedTexImage2D, compressedTexImage3D,
       compressedTexSubImage1D, compressedTexSubImage2D, compressedTexSubImage3D,
       getCompressedTexImage,

      -- * Implementation-Dependent Limits
      maxTextureSize
    ) where

    import Data.StateVar
    import Foreign.Marshal.Array
    import Foreign.Ptr
    import Graphics.Rendering.OpenGL.GL.CoordTrans
    import Graphics.Rendering.OpenGL.GL.PixelData
    import Graphics.Rendering.OpenGL.GL.PixelRectangles
    import Graphics.Rendering.OpenGL.GL.QueryUtils
    import Graphics.Rendering.OpenGL.GL.Texturing.PixelInternalFormat
    import Graphics.Rendering.OpenGL.GL.Texturing.TextureTarget
    import Graphics.Rendering.OpenGL.Raw.Core31

    --------------------------------------------------------------------------------

    type Level = GLint

    type Border = GLint

    newtype TexturePosition1D = TexturePosition1D GLint
       deriving ( Eq, Ord, Show )

    data TexturePosition2D = TexturePosition2D !GLint !GLint
       deriving ( Eq, Ord, Show )

    data TexturePosition3D = TexturePosition3D !GLint !GLint !GLint
       deriving ( Eq, Ord, Show )

    newtype TextureSize1D = TextureSize1D GLsizei
       deriving ( Eq, Ord, Show )

    data TextureSize2D = TextureSize2D !GLsizei !GLsizei
       deriving ( Eq, Ord, Show )

    data TextureSize3D = TextureSize3D !GLsizei !GLsizei !GLsizei
       deriving ( Eq, Ord, Show )

    --------------------------------------------------------------------------------

    texImage1D :: Proxy -> Level -> PixelInternalFormat -> TextureSize1D -> Border -> PixelData a -> IO ()
    texImage1D proxy level int (TextureSize1D w) border pd =
       withPixelData pd $
          glTexImage1D
             (marshalProxyTextureTarget proxy Texture1D)
             level (marshalPixelInternalFormat int) w border

    --------------------------------------------------------------------------------

    texImage2D :: Maybe CubeMapTarget -> Proxy -> Level -> PixelInternalFormat -> TextureSize2D -> Border -> PixelData a -> IO ()
    texImage2D mbCubeMap proxy level int (TextureSize2D w h) border pd =
       withPixelData pd $
          glTexImage2D
             (maybe (marshalProxyTextureTarget proxy Texture2D)
                    (\c -> if proxy == Proxy then marshalProxyTextureTarget Proxy TextureCubeMap else marshalCubeMapTarget c)
                    mbCubeMap)
             level (marshalPixelInternalFormat int) w h border

    --------------------------------------------------------------------------------

    texImage3D :: Proxy -> Level -> PixelInternalFormat -> TextureSize3D -> Border -> PixelData a -> IO ()
    texImage3D proxy level int (TextureSize3D w h d) border pd =
       withPixelData pd $
          glTexImage3D
             (marshalProxyTextureTarget proxy Texture3D)
             level (marshalPixelInternalFormat int) w h d border

    --------------------------------------------------------------------------------

    getTexImage :: Either TextureTarget CubeMapTarget -> Level -> PixelData a -> IO ()
    getTexImage t level pd =
       withPixelData pd $
          glGetTexImage (either marshalTextureTarget marshalCubeMapTarget t) level

    --------------------------------------------------------------------------------

    copyTexImage1D :: Level -> PixelInternalFormat -> Position -> TextureSize1D -> Border -> IO ()
    copyTexImage1D level int (Position x y) (TextureSize1D w) border =
       glCopyTexImage1D
          (marshalTextureTarget Texture1D) level
          (marshalPixelInternalFormat' int) x y w border

    --------------------------------------------------------------------------------

    copyTexImage2D :: Maybe CubeMapTarget -> Level -> PixelInternalFormat -> Position -> TextureSize2D -> Border -> IO ()
    copyTexImage2D mbCubeMap level int (Position x y) (TextureSize2D w h) border =
       glCopyTexImage2D
          (maybe (marshalTextureTarget Texture2D) marshalCubeMapTarget mbCubeMap) level
          (marshalPixelInternalFormat' int) x y w h border

    --------------------------------------------------------------------------------

    texSubImage1D :: Level -> TexturePosition1D -> TextureSize1D -> PixelData a -> IO ()
    texSubImage1D level (TexturePosition1D xOff) (TextureSize1D w) pd =
       withPixelData pd $
          glTexSubImage1D (marshalTextureTarget Texture1D) level xOff w

    --------------------------------------------------------------------------------

    texSubImage2D :: Maybe CubeMapTarget -> Level -> TexturePosition2D -> TextureSize2D -> PixelData a -> IO ()
    texSubImage2D mbCubeMap level (TexturePosition2D xOff yOff) (TextureSize2D w h) pd =
       withPixelData pd $
          glTexSubImage2D (maybe (marshalTextureTarget Texture2D) marshalCubeMapTarget mbCubeMap) level xOff yOff w h

    --------------------------------------------------------------------------------

    texSubImage3D :: Level -> TexturePosition3D -> TextureSize3D -> PixelData a -> IO ()
    texSubImage3D level (TexturePosition3D xOff yOff zOff) (TextureSize3D w h d) pd =
       withPixelData pd $
          glTexSubImage3D (marshalTextureTarget Texture3D) level xOff yOff zOff w h d

    --------------------------------------------------------------------------------

    copyTexSubImage1D :: Level -> TexturePosition1D -> Position -> TextureSize1D -> IO ()
    copyTexSubImage1D level (TexturePosition1D xOff) (Position x y) (TextureSize1D w) =
       glCopyTexSubImage1D (marshalTextureTarget Texture1D) level xOff x y w

    --------------------------------------------------------------------------------

    copyTexSubImage2D :: Maybe CubeMapTarget -> Level -> TexturePosition2D -> Position -> TextureSize2D -> IO ()
    copyTexSubImage2D mbCubeMap level (TexturePosition2D xOff yOff) (Position x y) (TextureSize2D w h) =
       glCopyTexSubImage2D (maybe (marshalTextureTarget Texture2D) marshalCubeMapTarget mbCubeMap) level xOff yOff x y w h

    --------------------------------------------------------------------------------

    copyTexSubImage3D :: Level -> TexturePosition3D -> Position -> TextureSize2D -> IO ()
    copyTexSubImage3D level (TexturePosition3D xOff yOff zOff) (Position x y) (TextureSize2D w h) =
       glCopyTexSubImage3D (marshalTextureTarget Texture3D) level xOff yOff zOff x y w h

    --------------------------------------------------------------------------------

    newtype CompressedTextureFormat = CompressedTextureFormat GLenum
       deriving ( Eq, Ord, Show )

    compressedTextureFormats :: GettableStateVar [CompressedTextureFormat]
    compressedTextureFormats =
       makeGettableStateVar $ do
          n <- getInteger1 fromIntegral GetNumCompressedTextureFormats
          allocaArray n $ \buf -> do
             getIntegerv GetCompressedTextureFormats buf
             fmap (map (CompressedTextureFormat . fromIntegral)) $ peekArray n buf

    --------------------------------------------------------------------------------

    data CompressedPixelData a =
         CompressedPixelData !CompressedTextureFormat GLsizei (Ptr a)
       deriving ( Eq, Ord, Show )

    withCompressedPixelData ::
       CompressedPixelData a -> (GLenum -> GLsizei -> Ptr a -> b) -> b
    withCompressedPixelData
       (CompressedPixelData (CompressedTextureFormat fmt) size ptr) f =
       f fmt size ptr

    --------------------------------------------------------------------------------

    compressedTexImage1D :: Proxy -> Level -> TextureSize1D -> Border -> CompressedPixelData a -> IO ()
    compressedTexImage1D proxy level (TextureSize1D w) border cpd =
       withCompressedPixelData cpd $ \fmt ->
          glCompressedTexImage1D
             (marshalProxyTextureTarget proxy Texture1D) level fmt w border

    --------------------------------------------------------------------------------

    compressedTexImage2D :: Maybe CubeMapTarget -> Proxy -> Level -> TextureSize2D -> Border -> CompressedPixelData a -> IO ()
    compressedTexImage2D mbCubeMap proxy level (TextureSize2D w h) border cpd =
       withCompressedPixelData cpd $ \fmt ->
          glCompressedTexImage2D
             (maybe (marshalProxyTextureTarget proxy Texture2D)
                    (\c -> if proxy == Proxy then marshalProxyTextureTarget Proxy TextureCubeMap else marshalCubeMapTarget c)
                    mbCubeMap)
             level fmt w h border

    --------------------------------------------------------------------------------

    compressedTexImage3D :: Proxy -> Level -> TextureSize3D -> Border -> CompressedPixelData a -> IO ()
    compressedTexImage3D proxy level (TextureSize3D w h d) border cpd =
       withCompressedPixelData cpd $ \fmt ->
          glCompressedTexImage3D
             (marshalProxyTextureTarget proxy Texture3D) level fmt w h d border

    --------------------------------------------------------------------------------

    getCompressedTexImage :: Either TextureTarget CubeMapTarget -> Level -> Ptr a -> IO ()
    getCompressedTexImage = glGetCompressedTexImage . either marshalTextureTarget marshalCubeMapTarget

    --------------------------------------------------------------------------------

    compressedTexSubImage1D :: Level -> TexturePosition1D -> TextureSize1D -> CompressedPixelData a -> IO ()
    compressedTexSubImage1D level (TexturePosition1D xOff) (TextureSize1D w) cpd =
       withCompressedPixelData cpd $
          glCompressedTexSubImage1D (marshalTextureTarget Texture1D) level xOff w

    --------------------------------------------------------------------------------

    compressedTexSubImage2D :: Maybe CubeMapTarget -> Level -> TexturePosition2D -> TextureSize2D -> CompressedPixelData a -> IO ()
    compressedTexSubImage2D mbCubeMap level (TexturePosition2D xOff yOff) (TextureSize2D w h) cpd =
       withCompressedPixelData cpd $
          glCompressedTexSubImage2D (maybe (marshalTextureTarget Texture2D) marshalCubeMapTarget mbCubeMap) level xOff yOff w h

    --------------------------------------------------------------------------------

    compressedTexSubImage3D :: Level -> TexturePosition3D -> TextureSize3D -> CompressedPixelData a -> IO ()
    compressedTexSubImage3D level (TexturePosition3D xOff yOff zOff) (TextureSize3D w h d) cpd =
       withCompressedPixelData cpd $
          glCompressedTexSubImage3D (marshalTextureTarget Texture3D) level xOff yOff zOff w h d

    --------------------------------------------------------------------------------

    maxTextureSize :: TextureTarget -> GettableStateVar GLsizei
    maxTextureSize = makeGettableStateVar . getInteger1 fromIntegral . textureTargetToMaxQuery

    textureTargetToMaxQuery :: TextureTarget -> GetPName
    textureTargetToMaxQuery x = case x of
       Texture1D -> GetMaxTextureSize
       Texture2D -> GetMaxTextureSize
       Texture3D -> GetMax3DTextureSize
       TextureCubeMap -> GetMaxCubeMapTextureSize
       TextureRectangle -> GetMaxRectangleTextureSize