module Sound.OpenAL.ALC.Capture (
   NumSamples, captureOpenDevice, captureStart, captureNumSamples,
   captureSamples, captureStop, captureCloseDevice,
   captureDefaultDeviceSpecifier, captureDeviceSpecifier,
   allCaptureDeviceSpecifiers
) where
import Graphics.Rendering.OpenGL.GL.StateVar
import Foreign.C.Types
import Foreign.Ptr ( Ptr, nullPtr, FunPtr )
import Sound.OpenAL.AL.Buffer ( Format )
import Sound.OpenAL.AL.Format ( marshalFormat )
import Sound.OpenAL.ALC.ALCboolean ( unmarshalALCboolean )
import Sound.OpenAL.ALC.BasicTypes (
   ALCchar, ALCuint, ALCenum, ALCsizei, ALCboolean )
import Sound.OpenAL.ALC.Context ( Frequency )
import Sound.OpenAL.ALC.Device ( Device )
import Sound.OpenAL.ALC.Extensions ( alcProcAddress )
import Sound.OpenAL.ALC.QueryUtils ( IntQuery(..), getInteger )
import Sound.OpenAL.ALC.QueryUtils (
   StringQuery(..), getString, getStringRaw, alcIsExtensionPresent )
import Sound.OpenAL.ALC.String ( withALCString, peekALCStrings )
import Sound.OpenAL.Config ( ALCdevice(..), marshalDevice, unmarshalDevice )
type NumSamples = ALCsizei
type Invoker a = FunPtr a -> a
getCaptureFunc :: String -> IO (FunPtr a)
getCaptureFunc = get . alcProcAddress Nothing
captureOpenDevice ::
   Maybe String -> Frequency -> Format -> NumSamples -> IO (Maybe Device)
captureOpenDevice maybeDeviceSpec frequency format size = do
   funPtr <- getCaptureFunc "alcCaptureOpenDevice"
   let open deviceSpec =
          invokeCaptureOpenDevice funPtr deviceSpec (round frequency)
                                  (fromIntegral (marshalFormat format)) size
   fmap unmarshalDevice $
      (maybe (open nullPtr)   
             (flip withALCString open)
             maybeDeviceSpec)
foreign import ccall unsafe "dynamic"
   invokeCaptureOpenDevice :: Invoker (Ptr ALCchar -> ALCuint -> ALCenum -> ALCsizei -> IO ALCdevice)
captureStart :: Device -> IO ()
captureStart = captureStartStop "alcCaptureStart"
captureStartStop :: String -> Device -> IO ()
captureStartStop funName device = do
   funPtr <- getCaptureFunc funName
   invokeCaptureStartStop funPtr (marshalDevice device)
foreign import ccall unsafe "dynamic"
   invokeCaptureStartStop :: Invoker (ALCdevice -> IO ()) 
captureNumSamples :: Device -> GettableStateVar NumSamples
captureNumSamples device = makeGettableStateVar $
   fmap fromIntegral (getInteger (Just device) CaptureSamples)
captureSamples :: Device -> Ptr a -> NumSamples -> IO ()
captureSamples device buf n = do
   funPtr <- getCaptureFunc "alcCaptureSamples"
   invokeCaptureSamples funPtr (marshalDevice device) buf n
foreign import ccall unsafe "dynamic"
   invokeCaptureSamples :: Invoker (ALCdevice -> Ptr a -> NumSamples -> IO ())
captureStop :: Device -> IO ()
captureStop = captureStartStop "alcCaptureStop"
captureCloseDevice :: Device -> IO Bool
captureCloseDevice device = do
   funPtr <- getCaptureFunc "alcCaptureCloseDevice"
   fmap unmarshalALCboolean .
      invokeCaptureCloseDevice funPtr . marshalDevice $ device
foreign import ccall unsafe "dynamic"
   invokeCaptureCloseDevice :: Invoker (ALCdevice -> IO ALCboolean) 
captureDefaultDeviceSpecifier :: GettableStateVar String
captureDefaultDeviceSpecifier = makeGettableStateVar $
   getString Nothing CaptureDefaultDeviceSpecifier
captureDeviceSpecifier :: Device -> GettableStateVar String
captureDeviceSpecifier device = makeGettableStateVar $
   getString (Just device) CaptureDeviceSpecifier
allCaptureDeviceSpecifiers :: GettableStateVar [String]
allCaptureDeviceSpecifiers = makeGettableStateVar $ do
   enumExtPresent <- get (alcIsExtensionPresent Nothing "ALC_ENUMERATION_EXT")
   if enumExtPresent
      then peekALCStrings =<< getStringRaw Nothing CaptureDeviceSpecifier
      else fmap (\s -> [s]) $ get captureDefaultDeviceSpecifier