    --------------------------------------------------------------------------------
    -- |
    -- Module      :  Sound.OpenAL.AL.Errors
    -- Copyright   :  (c) Sven Panne 2003-2005
    -- License     :  BSD-style (see the file libraries/OpenAL/LICENSE)
    --
    -- Maintainer  :  sven.panne@aedion.de
    -- Stability   :  provisional
    -- Portability :  portable
    --
    -- This module corresponds to section 2.7 (AL Errors) of the OpenAL
    -- Specification and Reference (version 1.1).
    --
    --------------------------------------------------------------------------------

    module Sound.OpenAL.AL.Errors (
       ALError(..), ALErrorCategory(..), alErrors
    ) where

    import Graphics.Rendering.OpenGL.GL.StateVar (
       GettableStateVar, makeGettableStateVar )
    import Sound.OpenAL.AL.BasicTypes ( ALenum )
    import Sound.OpenAL.Constants (
       al_NO_ERROR, al_INVALID_NAME, al_INVALID_ENUM, al_INVALID_VALUE,
       al_INVALID_OPERATION, al_OUT_OF_MEMORY )
    import Sound.OpenAL.AL.QueryUtils ( StringName(..), getString )

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

    -- | AL errors consist of a general error category and a description of what
    -- went wrong.

    data ALError = ALError ALErrorCategory String
       deriving ( Eq, Ord, Show )

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

    -- | General AL error categories.

    data ALErrorCategory =
         ALInvalidEnum
       | ALInvalidValue
       | ALInvalidOperation
       | ALInvalidName
       | ALOutOfMemory
       deriving ( Eq, Ord, Show )

    unmarshalALErrorCategory :: ALenum -> ALErrorCategory
    unmarshalALErrorCategory x
       | x == al_INVALID_ENUM = ALInvalidEnum
       | x == al_INVALID_VALUE = ALInvalidValue
       | x == al_INVALID_OPERATION = ALInvalidOperation
       | x == al_INVALID_NAME = ALInvalidName
       | x == al_OUT_OF_MEMORY = ALOutOfMemory
       | otherwise = error ("unmarshalALErrorCategory: illegal value " ++ show x)

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

    -- | OpenAL detects only a subset of those conditions that could be considered
    -- errors. This is because in many cases error checking would adversely impact
    -- the performance of an error-free program. The state variable 'alErrors' is
    -- used to obtain error information. When an error is detected by AL, a flag is
    -- set and the error code is recorded. Further errors, if they occur, do not
    -- affect this recorded code. When 'alErrors' is read, the error is returned and
    -- the flag is cleared, so that a further error will again record its code. If
    -- reading 'alErrors' returns @\[\]@ then there has been no detectable error
    -- since the last time 'alErrors' (or since the AL was initialized).
    --
    -- When an error flag is set, results of AL operations are undefined only if
    -- 'ALOutOfMemory' has occurred. In other cases, the command generating the
    -- error is ignored so that it has no effect on AL state or output buffer
    -- contents. If the error generating command returns a value, it returns zero.
    -- If the generating command modifies values through a pointer argument, no
    -- change is made to these values. These error semantics apply only to AL
    -- errors, not to system errors such as memory access errors.

    alErrors :: GettableStateVar [ALError]
    alErrors =
       makeGettableStateVar $ do
          c <- alGetError
          if c == al_NO_ERROR
             then return []
             else do
                s <- getString (ALErrorCategory c)
                return [ ALError (unmarshalALErrorCategory c) s ]

    foreign import CALLCONV unsafe "alGetError"
       alGetError :: IO ALenum