    --------------------------------------------------------------------------------
    -- |
    -- Module      :  Graphics.Rendering.OpenGL.GL.Fog
    -- 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.10 (Fog) of the OpenGL 2.1 specs.
    --
    --------------------------------------------------------------------------------

    module Graphics.Rendering.OpenGL.GL.Fog (
       fog,
       FogMode(..), fogMode,
       fogColor, fogIndex,
       FogCoordSrc(..), fogCoordSrc,
       FogDistanceMode(..), fogDistanceMode
    ) where

    import Data.StateVar
    import Foreign.Marshal.Utils
    import Foreign.Ptr
    import Graphics.Rendering.OpenGL.GL.Capability
    import Graphics.Rendering.OpenGL.GL.QueryUtils
    import Graphics.Rendering.OpenGL.GL.VertexSpec
    import Graphics.Rendering.OpenGL.Raw.ARB.Compatibility (
       glFogf, glFogfv, glFogi, gl_EXP, gl_EXP2, gl_EYE_PLANE, gl_FOG_COLOR,
       gl_FOG_COORD, gl_FOG_COORD_SRC, gl_FOG_DENSITY, gl_FOG_END, gl_FOG_INDEX,
       gl_FOG_MODE, gl_FOG_START, gl_FRAGMENT_DEPTH )
    import Graphics.Rendering.OpenGL.Raw.Core31

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

    fog :: StateVar Capability
    fog = makeCapability CapFog

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

    data FogParameter =
         FogIndex
       | FogDensity
       | FogStart
       | FogEnd
       | FogMode
       | FogColor
       | FogCoordSrc
       | FogDistanceMode

    marshalFogParameter :: FogParameter -> GLenum
    marshalFogParameter x = case x of
       FogIndex -> gl_FOG_INDEX
       FogDensity -> gl_FOG_DENSITY
       FogStart -> gl_FOG_START
       FogEnd -> gl_FOG_END
       FogMode -> gl_FOG_MODE
       FogColor -> gl_FOG_COLOR
       FogCoordSrc -> gl_FOG_COORD_SRC
       -- TODO: Use FOG_DISTANCE_MODE_NV from NV_fog_distance extension
       FogDistanceMode -> 0x855a

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

    data FogMode' =
         Linear'
       | Exp'
       | Exp2'

    marshalFogMode' :: FogMode' -> GLint
    marshalFogMode' x = fromIntegral $ case x of
       Linear' -> gl_LINEAR
       Exp' -> gl_EXP
       Exp2' -> gl_EXP2

    unmarshalFogMode' :: GLint -> FogMode'
    unmarshalFogMode' x
       | y == gl_LINEAR = Linear'
       | y == gl_EXP = Exp'
       | y == gl_EXP2 = Exp2'
       | otherwise = error ("unmarshalFogMode': illegal value " ++ show x)
       where y = fromIntegral x

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

    data FogMode =
         Linear GLfloat GLfloat
       | Exp GLfloat
       | Exp2 GLfloat
       deriving ( Eq, Ord, Show )

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

    fogMode :: StateVar FogMode
    fogMode = makeStateVar getFogMode setFogMode

    getFogMode :: IO FogMode
    getFogMode = do
       mode <- getInteger1 unmarshalFogMode' GetFogMode
       case mode of
          Linear' -> do
             start <- getFloat1 id GetFogStart
             end <- getFloat1 id GetFogEnd
             return $ Linear start end
          Exp' -> getFloat1 Exp GetFogDensity
          Exp2' -> getFloat1 Exp2 GetFogDensity

    setFogMode :: FogMode -> IO ()
    setFogMode (Linear start end) = do
       fogi FogMode (marshalFogMode' Linear')
       fogf FogStart start
       fogf FogEnd end
    setFogMode (Exp density) = do
       fogi FogMode (marshalFogMode' Exp')
       fogf FogDensity density
    setFogMode (Exp2 density) = do
       fogi FogMode (marshalFogMode' Exp2')
       fogf FogDensity density

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

    fogi :: FogParameter -> GLint -> IO ()
    fogi = glFogi . marshalFogParameter

    fogf :: FogParameter -> GLfloat -> IO ()
    fogf = glFogf . marshalFogParameter

    fogfv :: FogParameter -> Ptr (Color4 GLfloat) -> IO ()
    fogfv param ptr = glFogfv (marshalFogParameter param) (castPtr ptr)

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

    fogColor :: StateVar (Color4 GLclampf)
    fogColor =
       makeStateVar
          (getClampf4 Color4 GetFogColor)
          (\c -> with c $ (fogfv FogColor . castPtr))

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

    fogIndex :: StateVar (Index1 GLint)
    fogIndex =
       makeStateVar
          (getInteger1 Index1 GetFogIndex)
          (\(Index1 i) -> fogi FogIndex i)

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

    data FogCoordSrc =
         FogCoord
       | FragmentDepth
       deriving ( Eq, Ord, Show )

    marshalFogCoordSrc :: FogCoordSrc -> GLint
    marshalFogCoordSrc x = fromIntegral $ case x of
       FogCoord -> gl_FOG_COORD
       FragmentDepth -> gl_FRAGMENT_DEPTH

    unmarshalFogCoordSrc :: GLint -> FogCoordSrc
    unmarshalFogCoordSrc x
       | y == gl_FOG_COORD = FogCoord
       | y == gl_FRAGMENT_DEPTH = FragmentDepth
       | otherwise = error ("unmarshalFogCoordSrc: illegal value " ++ show x)
       where y = fromIntegral x

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

    fogCoordSrc :: StateVar FogCoordSrc
    fogCoordSrc =
       makeStateVar
          (getInteger1 unmarshalFogCoordSrc GetFogCoordSrc)
          (fogi FogCoordSrc . marshalFogCoordSrc)

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

    data FogDistanceMode =
         EyeRadial
       | EyePlaneSigned
       | EyePlaneAbsolute
       deriving ( Eq, Ord, Show )

    marshalFogDistanceMode :: FogDistanceMode -> GLint
    marshalFogDistanceMode x = fromIntegral $ case x of
       -- TODO: Use EYE_RADIAL_NV from NV_fog_distance extension
       EyeRadial -> 0x855b
       EyePlaneSigned ->gl_EYE_PLANE
       -- TODO: Use EYE_PLANE_ABSOLUTE_NV from NV_fog_distance extension
       EyePlaneAbsolute -> 0x855c

    unmarshalFogDistanceMode :: GLint -> FogDistanceMode
    unmarshalFogDistanceMode x
       -- TODO: Use EYE_RADIAL_NV from NV_fog_distance extension
       | y == 0x855b = EyeRadial
       | y == gl_EYE_PLANE = EyePlaneSigned
       -- TODO: Use EYE_PLANE_ABSOLUTE_NV from NV_fog_distance extension
       | y == 0x855c = EyePlaneAbsolute
       | otherwise = error ("unmarshalFogDistanceMode: illegal value " ++ show x)
       where y = fromIntegral x

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

    fogDistanceMode :: StateVar FogDistanceMode
    fogDistanceMode =
       makeStateVar
          (getInteger1 unmarshalFogDistanceMode GetFogDistanceMode)
          (fogi FogDistanceMode . marshalFogDistanceMode)