-- #hide
--------------------------------------------------------------------------------
-- |
-- Module      :  Sound.OpenAL.Config
-- Copyright   :  (c) Sven Panne 2005
-- License     :  BSD-style (see the file libraries/OpenAL/LICENSE)
-- 
-- Maintainer  :  sven.panne@aedion.de
-- Stability   :  provisional
-- Portability :  portable
--
-- This purely internal module defines the platform-specific stuff which has
-- been figured out by configure.
--
--------------------------------------------------------------------------------

module Sound.OpenAL.Config (
   -- AL types
   ALboolean, ALchar, ALbyte, ALubyte, ALshort, ALushort, ALint, ALuint,
   ALsizei, ALenum, ALfloat, ALdouble,

   -- ALC types
   ALCboolean, ALCchar, ALCbyte, ALCubyte, ALCshort, ALCushort, ALCint, ALCuint,
   ALCsizei, ALCenum, ALCfloat, ALCdouble,

   -- Device stuff
   ALCdevice, Device, nullDevice, marshalDevice, unmarshalDevice, closeDevice,

   -- Context stuff
   ALCcontext, Context, nullContext, marshalContext, unmarshalContext,
   alcProcessContext, alcMakeContextCurrent, alcDestroyContext
) where

import Data.Int
import Data.Word
import Foreign.Ptr ( Ptr, nullPtr )

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

#include "HsOpenALConfig.h"

--------------------------------------------------------------------------------
-- AL types

-- | 8-bit boolean
type ALboolean = HTYPE_ALBOOLEAN

-- | Character
type ALchar = HTYPE_ALCHAR

-- | Signed 8-bit 2\'s complement integer
type ALbyte = HTYPE_ALBYTE

-- | Unsigned 8-bit integer
type ALubyte = HTYPE_ALUBYTE

-- | Signed 16-bit 2\'s complement integer
type ALshort = HTYPE_ALSHORT

-- | Unsigned 16-bit integer
type ALushort = HTYPE_ALUSHORT

-- | Signed 32-bit 2\'s complement integer
type ALint = HTYPE_ALINT

-- | Unsigned 32-bit integer
type ALuint = HTYPE_ALUINT

-- | Non-negatitve 32-bit binary integer size
type ALsizei = HTYPE_ALSIZEI

-- | Enumerated 32-bit value
type ALenum = HTYPE_ALENUM

-- | 32-bit IEEE754 floating-point
type ALfloat = HTYPE_ALFLOAT

-- | 64-bit IEEE754 floating-point
type ALdouble = HTYPE_ALDOUBLE

--------------------------------------------------------------------------------
-- ALC types

-- | 8-bit boolean
type ALCboolean = HTYPE_ALCBOOLEAN

-- | Character
type ALCchar = HTYPE_ALCCHAR

-- | Signed 8-bit 2\'s complement integer
type ALCbyte = HTYPE_ALCBYTE

-- | Unsigned 8-bit integer
type ALCubyte = HTYPE_ALCUBYTE

-- | Signed 16-bit 2\'s complement integer
type ALCshort = HTYPE_ALCSHORT

-- | Unsigned 16-bit integer
type ALCushort = HTYPE_ALCUSHORT

-- | Signed 32-bit 2\'s complement integer
type ALCint = HTYPE_ALCINT

-- | Unsigned 32-bit integer
type ALCuint = HTYPE_ALCUINT

-- | Non-negatitve 32-bit binary integer size
type ALCsizei = HTYPE_ALCSIZEI

-- | Enumerated 32-bit value
type ALCenum = HTYPE_ALCENUM

-- | 32-bit IEEE754 floating-point
type ALCfloat = HTYPE_ALCFLOAT

-- | 64-bit IEEE754 floating-point
type ALCdouble = HTYPE_ALCDOUBLE

--------------------------------------------------------------------------------
-- In OpenAL 1.1, alcCloseDevice() returns an ALCboolean, before it was void.
-- To break a dependency cycle, we have to define the Device type here, too.

-- | The abstract device type.

newtype Device = Device ALCdevice
   deriving ( Eq, Ord, Show )

newtype ALCdevice = ALCdevice (Ptr ALCdevice)
   deriving ( Eq, Ord, Show )

nullDevice :: Device
nullDevice = Device (ALCdevice nullPtr)

marshalDevice :: Device -> ALCdevice
marshalDevice (Device device) = device

unmarshalDevice :: ALCdevice -> Maybe Device
unmarshalDevice device =
   if device == marshalDevice nullDevice then Nothing else Just (Device device)

-- | 'closeDevice' allows the application (i.e. the client program) to
-- disconnect from a device (i.e. the server). It returns 'True' for success and
-- 'False' for failure. Once closed, the 'Device' is invalid.
--
-- /Note:/ Older OpenAL implementations will always report a success!

closeDevice :: Device -> IO Bool

#if ALCCLOSEDEVICE_VOID

closeDevice = fmap (const True) . alcCloseDevice . marshalDevice

foreign import CALLCONV unsafe "alcCloseDevice"
   alcCloseDevice :: ALCdevice -> IO ()

#else

-- inlined unmarshalALCboolean here to break dependency cycle
closeDevice = fmap (/= CONST_ALC_FALSE) . alcCloseDevice . marshalDevice

foreign import CALLCONV unsafe "alcCloseDevice"
   alcCloseDevice :: ALCdevice -> IO ALCboolean

#endif

--------------------------------------------------------------------------------
-- In OpenAL 1.1, alcProcessContext() returns void for all platforms, before it
-- returned ALCcontext* on Linux. To break a dependency cycle, we have to define
-- the Context type here, too.

-- | The abstract context type.

data Context = Context ALCcontext
   deriving ( Eq, Ord, Show )

newtype ALCcontext = ALCcontext (Ptr ALCcontext)
   deriving ( Eq, Ord, Show )

nullContext :: Context
nullContext = Context (ALCcontext nullPtr)

marshalContext :: Context -> ALCcontext
marshalContext (Context context) = context

unmarshalContext :: ALCcontext -> Maybe Context
unmarshalContext context =
   if context == marshalContext nullContext then Nothing else Just (Context context)

#if ALCPROCESSCONTEXT_VOID

foreign import CALLCONV unsafe "alcProcessContext"
   alcProcessContext :: ALCcontext -> IO ()

#else

foreign import CALLCONV unsafe "alcProcessContext"
   alcProcessContext :: ALCcontext -> IO ALCcontext

#endif

--------------------------------------------------------------------------------
-- In OpenAL 1.1, alcMakeContextCurrent() returns void, before it was ALCenum on
-- Linux and ALCboolean on other platforms. Currently we default to ALCenum in
-- the latter case.

#if ALCMAKECONTEXTCURRENT_VOID

foreign import CALLCONV unsafe "alcMakeContextCurrent"
   alcMakeContextCurrent :: ALCcontext -> IO ()

#else

foreign import CALLCONV unsafe "alcMakeContextCurrent"
   alcMakeContextCurrent :: ALCcontext -> IO ALCenum

#endif

--------------------------------------------------------------------------------
-- In OpenAL 1.1, alcDestroyContext() returns void, before it returned ALCenum
-- on Linux.

#if ALCDESTROYCONTEXT_VOID

foreign import CALLCONV unsafe "alcDestroyContext"
   alcDestroyContext :: ALCcontext -> IO ()

#else

foreign import CALLCONV unsafe "alcDestroyContext"
   alcDestroyContext :: ALCcontext -> IO ALCenum

#endif