-- Copyright (c) 2010 Philipp Balzarek (p.balzarek@googlemail.com)
--
-- License: MIT; See LICENSE file

{-# LANGUAGE ForeignFunctionInterface #-}

-- | Low level bindings to the JACK \<jack.h\> functionality
--
-- This is a faithfull representation of the C api. 
-- For now please refer to the JACK api documentation for a complete 
-- description of the functionality  
--
-- <http://jackaudio.org/files/docs/html/jack_8h.html>
--
-- A somewhat safer interface can be found at "Sound.Jack.JackMonad" 

module Sound.Jack.Bindings 
  ( withOpenClient
  , withOpenClientDefaultServer
  , Raw.clientOpenWithDefaultServer
  , Raw.clientOpenWithServerName
  , Raw.clientClose
  , Raw.clientNameSize
  , Raw.getClientName
  , Raw.internalClientNew
  , Raw.internalClientClose
  , Raw.activate
  , Raw.deactivate
  , Raw.clientThreadId
  , Raw.isRealtime
  , Raw.cycleWait
  , Raw.cycleSignal
  , setProcessThread
  , setThreadInitCallback
  , setProcessCallback
  , setFreewheelCallback
  , setBufferSizeCallback
  , setSampleRateCallback
  , setClientRegistrationCallback
  , setPortRegistrationCallback
  , setPortConnectCallback
  , setGraphOrderCallback
  , setXrunCallback
  , Raw.setFreewheel
  , Raw.setBufferSize
  , Raw.getSampleRate
  , Raw.getBufferSize
  , Raw.engineTakeoverTimebase
  , Raw.cpuLoad
  , Raw.portRegister
  , Raw.portUnregister
  , Raw.portGetBuffer
  , Raw.portName
  , Raw.portShortName
  , Raw.portFlags
  , Raw.portType
  , Raw.portIsMine
  , Raw.portConnected
  , Raw.portGetConnections
  , Raw.portGetAllConnections
  , Raw.jackPortGetLatency
  , Raw.jackPortGetTotalLatency
  , Raw.portSetLatency
  , Raw.recomputeTotalLatencies
  , Raw.portSetName
  , Raw.portSetAlias
  , Raw.portUnsetAlias
  , Raw.portRequestMonitor
  , Raw.portRequestMonitorByName
  , Raw.portEnsureMonitor
  , Raw.portMonitoringInput
  , Raw.connect
  , Raw.disconnect
  , Raw.portDisconnect
  , Raw.portNameSize
  , Raw.portTypeSize
  , Raw.getPorts
  , Raw.portByName
  , Raw.portById
  , Raw.framesSinceCycleStart
  , Raw.frameTime
  , Raw.framesToTime
  , Raw.timeToFrames
  , Raw.getTime

  , Raw.Port
  , Raw.PortFlags(..)
  , Raw.AudioSample
  , BufferSizeCallback
  , ClientRegistrationCallback
  , FreewheelCallback
  , GraphOrderCallback
  , PortConnectCallback
  , PortRegistrationCallback
  , ProcessCallback
  , SampleRateCallback
  , ThreadCallback
  , ThreadInitCallback
  , XRunCallback
  , Raw.Options(..)
  , Raw.Client                   -- opaque
  , Raw.Status(..)
  , Raw.PortID                   -- opaque
  , Raw.defaultAudioType
  , Raw.defaultMidiType
  , Raw.fromPThread
  , Raw.NFrames
  , Raw.PThread
  , Raw.Time
  )

       where

import qualified Sound.Jack.RawBindings as Raw
import Control.Monad
import Control.Exception
import Foreign
import Foreign.C

ignore = (>> return ())

-- | Open a Jack client and run the supplied action, handling errors and closing the client
withOpenClient :: String -- ^ server name 
  -> [Raw.Options] -- ^ client options
  -> String -- ^ client name
  -> (Raw.Client -> [Raw.Status] -> IO a)  -- ^ action to be run with the open client
  -> ([Raw.Status] -> IO a) -- ^ action to be run on error
  -> IO a  -- ^
withOpenClient serverName options clientName action errorHandler = bracket 
  (Raw.clientOpenWithServerName clientName options serverName )
  (\(client,flags) -> unless (Raw.Failure `elem` flags)  
                      (ignore $ Raw.clientClose client ))
  (\(client, flags) -> 
    if (Raw.Failure `elem` flags)
      then errorHandler flags
      else action client flags)

-- | Open a Jack client on the default server and run the supplied action, 
-- handling errors and closing the client
withOpenClientDefaultServer :: [Raw.Options] -- ^ client options
  -> String -- ^ The client name 
  -> (Raw.Client -> [Raw.Status] -> IO a)  -- ^ action to be run with the open client
  -> ([Raw.Status] -> IO a) -- ^ action to be run at error
  -> IO a  -- ^
withOpenClientDefaultServer options clientName action errorHandler = bracket 
  (Raw.clientOpenWithDefaultServer clientName options)
  (\(client,flags) -> 
       unless (Raw.Failure `elem` flags) (ignore $ Raw.clientClose client )) 
  (\(client, flags) -> if Raw.Failure `elem` flags
      then errorHandler flags
      else action client flags )

type ThreadCallback             = Ptr () -> IO (Ptr ())
type BufferSizeCallback         = CUInt -> Ptr () -> IO CInt
type ClientRegistrationCallback = Ptr CChar -> CInt -> Ptr () -> IO ()
type FreewheelCallback          = CInt -> Ptr () -> IO ()
type GraphOrderCallback         = Ptr () -> IO CInt
type PortConnectCallback        = CUInt -> CUInt -> CInt -> Ptr () -> IO ()
type PortRegistrationCallback   = CUInt -> CInt -> Ptr () -> IO ()

type ProcessCallback            = CUInt -- ^ Number of Frames to Process
                                  -> Ptr () -- ^ User Data
                                  -> IO CInt
type SampleRateCallback         = CUInt -> Ptr () -> IO CInt
type ThreadInitCallback         = Ptr () -> IO ()
type XRunCallback               = Ptr () -> IO CInt

foreign import ccall safe "wrapper" mkThreadCallback
  :: ThreadCallback            -> IO Raw.ThreadCallbackPtr            
foreign import ccall safe "wrapper" mkBufferSizeCallback        
  :: BufferSizeCallback        -> IO Raw.BufferSizeCallbackPtr        
foreign import ccall safe "wrapper" mkClientRegistrationCallback
  :: ClientRegistrationCallback-> IO Raw.ClientRegistrationCallbackPtr
foreign import ccall safe "wrapper" mkFreewheelCallback         
  :: FreewheelCallback         -> IO Raw.FreewheelCallbackPtr         
foreign import ccall safe "wrapper" mkGraphOrderCallback        
  :: GraphOrderCallback        -> IO Raw.GraphOrderCallbackPtr        
foreign import ccall safe "wrapper" mkPortConnectCallback       
  :: PortConnectCallback       -> IO Raw.PortConnectCallbackPtr       
foreign import ccall safe "wrapper" mkPortRegistrationCallback  
  :: PortRegistrationCallback  -> IO Raw.PortRegistrationCallbackPtr  
foreign import ccall safe "wrapper" mkProcessCallback           
  :: ProcessCallback           -> IO Raw.ProcessCallbackPtr           
foreign import ccall safe "wrapper" mkSampleRateCallback        
  :: SampleRateCallback        -> IO Raw.SampleRateCallbackPtr        
foreign import ccall safe "wrapper" mkThreadInitCallback        
  :: ThreadInitCallback        -> IO Raw.ThreadInitCallbackPtr        
foreign import ccall safe "wrapper" mkXRunCallback              
  :: XRunCallback              -> IO Raw.XRunCallbackPtr            


withFunPtr  :: (Monad m) =>
     (t1 -> m a) -> (t -> a -> t2 -> m b) -> t -> t1 -> t2 -> m b
withFunPtr mkPtr setCallback client callbackFun userData = 
  mkPtr callbackFun >>= \fPtr -> setCallback client fPtr userData

setProcessThread
  :: Raw.Client -> ThreadCallback -> Ptr () -> IO Int
setProcessThread              
  = withFunPtr mkThreadCallback              Raw.setProcessThread

setThreadInitCallback
  :: Raw.Client -> ThreadInitCallback -> Ptr () -> IO Int
setThreadInitCallback         
  = withFunPtr mkThreadInitCallback          Raw.setThreadInitCallback

setProcessCallback
  :: Raw.Client -> ProcessCallback -> Ptr () -> IO Int
setProcessCallback            
  = withFunPtr mkProcessCallback             Raw.setProcessCallback

setFreewheelCallback
  :: Raw.Client -> FreewheelCallback -> Ptr () -> IO Int
setFreewheelCallback          
  = withFunPtr mkFreewheelCallback           Raw.setFreewheelCallback

setBufferSizeCallback
  :: Raw.Client -> BufferSizeCallback -> Ptr () -> IO Int
setBufferSizeCallback         
  = withFunPtr mkBufferSizeCallback          Raw.setBufferSizeCallback

setSampleRateCallback
  :: Raw.Client -> SampleRateCallback -> Ptr () -> IO Int
setSampleRateCallback         
  = withFunPtr mkSampleRateCallback          Raw.setSampleRateCallback

setClientRegistrationCallback
  :: Raw.Client -> ClientRegistrationCallback -> Ptr () -> IO Int
setClientRegistrationCallback 
  = withFunPtr mkClientRegistrationCallback  Raw.setClientRegistrationCallback

setPortRegistrationCallback
  :: Raw.Client -> PortRegistrationCallback -> Ptr () -> IO Int
setPortRegistrationCallback   
  = withFunPtr mkPortRegistrationCallback    Raw.setPortRegistrationCallback

setPortConnectCallback
  :: Raw.Client -> PortConnectCallback -> Ptr () -> IO Int
setPortConnectCallback        
  = withFunPtr mkPortConnectCallback         Raw.setPortConnectCallback

setGraphOrderCallback
  :: Raw.Client -> GraphOrderCallback -> Ptr () -> IO Int
setGraphOrderCallback         
  = withFunPtr mkGraphOrderCallback          Raw.setGraphOrderCallback

setXrunCallback :: Raw.Client -> XRunCallback -> Ptr () -> IO Int
setXrunCallback               
  = withFunPtr mkXRunCallback                Raw.setXrunCallback