module Sound.ALSA.PCM.Node.ALSA (
   -- * Types
   Handle,
   PCM.Stream(..),
   PCM.Mode, PCM.modes, PCM.nonBlock, PCM.async,
   Interleaved, Noninterleaved,
   Time, SampleFreq, Size,
   -- * Classes
   Class.Access, Class.SampleFmt, Class.MonoSampleFmt,
   -- * Management of streams
   open,
   PCM.close,
   PCM.prepare,
   PCM.start,
   PCM.drop,
   PCM.drain,
   -- * Data transfer
   readi, writei, readiRetry, writeiRetry,
   readn, writen,
   ) where

import Sound.ALSA.PCM.Parameters.Hardware (Time, SampleFreq, Size, )
import Sound.ALSA.PCM.Core.Handle (Handle, Interleaved, Noninterleaved, prepare, )
import qualified Sound.ALSA.PCM.Parameters.Software as SwParam
import qualified Sound.ALSA.PCM.Parameters.Hardware as HwParam
import qualified Sound.ALSA.PCM.Core.Class as Class
import qualified Sound.ALSA.PCM.Core.Handle as PCM
import qualified Sound.ALSA.PCM.Debug as Debug
import qualified Sound.ALSA.Exception as AlsaExc

import Foreign.Marshal.Array (advancePtr, )
import Foreign (Ptr, )


open ::
   (Class.Access i, Class.SampleFmt y) =>
      PCM.Mode
   -> PCM.Stream
   -> HwParam.T i y a
   -> (a -> SwParam.T i y b)
   -> String -- ^ device, e.g @\"default\"@
   -> IO (b, Handle i y)
open mode stream hwp swp dev = do
   h <- PCM.open dev stream mode
   a <- Class.withHwParams h hwp
   b <- Class.withSwParams h $ swp a
   return (b, h)


readi ::
   (Class.SampleFmt y) =>
   Handle Interleaved y -> Ptr y -> Size -> IO Size
readi = PCM.readi

writei ::
   (Class.SampleFmt y) =>
   Handle Interleaved y -> Ptr y -> Size -> IO Size
writei = PCM.writei

{- |
The @Ptr (Ptr y)@ argument is actually a pointer to an array of pointers.
The array must have the size of number of channels.
In 'Noninterleaved' mode you must set the number of channels manually
using 'HwParam.setChannels' or its friends.
It is an unchecked error if the number of channels
set with 'HwParam.setChannels'
does not match the array size in the 'readn' call.
-}
readn ::
   (Class.MonoSampleFmt y) =>
   Handle Noninterleaved y -> Ptr (Ptr y) -> Size -> IO Size
readn = PCM.readn

{- |
Cf. 'readn'.
-}
writen ::
   (Class.MonoSampleFmt y) =>
   Handle Noninterleaved y -> Ptr (Ptr y) -> Size -> IO Size
writen = PCM.writen



{- |
retry on buffer over-run
-}
readiRetry ::
   Class.SampleFmt y =>
   Handle Interleaved y -> Ptr y -> Size -> IO Size
readiRetry h buf0 n =
   let go buf offset = do
          -- debug $ "Reading " ++ show n ++ " samples..."
          nread <-
             readi h buf (n-offset)
             `AlsaExc.catchXRun`
             do Debug.put "snd_pcm_readi reported buffer over-run"
                prepare h
                go buf offset
          let newOffset = offset+nread
          -- debug $ "Got " ++ show n' ++ " samples."
          if newOffset < n
            then go (advancePtr buf (fromIntegral nread)) newOffset
            else return newOffset
   in  go buf0 0

{- |
retry on buffer under-run
-}
writeiRetry ::
   Class.SampleFmt y =>
   Handle Interleaved y -> Ptr y -> Size -> IO Size
writeiRetry h buf0 n =
   let go buf offset = do
          -- debug $ "Writing " ++ show n ++ " samples..."
          nwritten <-
             writei h buf (n-offset)
             `AlsaExc.catchXRun`
             do Debug.put "snd_pcm_writei reported buffer under-run"
                prepare h
                go buf offset
          let newOffset = offset+nwritten
          --debug $ "Wrote " ++ show n' ++ " samples."
          if newOffset < n
            then go (advancePtr buf (fromIntegral nwritten)) newOffset
            else return newOffset
   in  go buf0 0