{-# LINE 1 "src/Sound/JACK/FFI.hsc" #-}
-- -*-haskell-*-
{-# LINE 2 "src/Sound/JACK/FFI.hsc" #-}
{-# LANGUAGE ForeignFunctionInterface #-}

{-
    JACK bindings for Haskell
    Copyright (C) 2011 Henning Thielemann
    Copyright (C) 2007 Soenke Hahn

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-}

module Sound.JACK.FFI where

import Foreign.Ptr (Ptr, FunPtr)
import Foreign.C.String (CString)
import qualified Foreign.C.Error as E
import qualified Foreign.C.Types as C

import qualified Data.EnumSet as ES
import Data.Word (Word, Word32, )
import Data.Ix (Ix(range, inRange, rangeSize, index))
import Data.Monoid (Monoid, mempty, mappend, )


{-# LINE 37 "src/Sound/JACK/FFI.hsc" #-}

data Client = Client
data Port typ = Port

foreign import ccall "static jack/jack.h jack_client_open"
  client_open :: CString -> OpenOptionSet -> Ptr StatusSet -> CString -> IO (Ptr Client)

{-# DEPRECATED client_new "use client_open instead" #-}
foreign import ccall "static jack/jack.h jack_client_new"
  client_new :: CString -> IO (Ptr Client)

foreign import ccall "static jack/jack.h jack_get_sample_rate"
  get_sample_rate :: Ptr Client -> IO C.CInt


type OpenOptionSet = ES.T C.CULong OpenOptions

data OpenOptions =
     NoStartServer
   | UseExactName
   | ServerName
     deriving (Enum, Eq, Ord, Show, Ix)

wordNullOption, wordNoStartServer, wordUseExactName, wordServerName :: OpenOptionSet
wordNullOption    = ES.empty
wordNoStartServer = ES.fromEnum NoStartServer
wordUseExactName  = ES.fromEnum UseExactName
wordServerName    = ES.fromEnum ServerName


type StatusSet = ES.T C.CULong Status

data Status =
     Failure
   | InvalidOption
   | NameNotUnique
   | ServerStarted
   | ServerFailed
   | ServerError
   | NoSuchClient
   | LoadFailure
   | InitFailure
   | ShmFailure
   | VersionError
     deriving (Enum, Eq, Ord, Show, Ix)

wordFailure, wordInvalidOption, wordNameNotUnique, wordServerStarted,
  wordServerFailed, wordServerError, wordNoSuchClient, wordLoadFailure,
  wordInitFailure, wordShmFailure, wordVersionError :: StatusSet
wordFailure       = ES.fromEnum Failure
wordInvalidOption = ES.fromEnum InvalidOption
wordNameNotUnique = ES.fromEnum NameNotUnique
wordServerStarted = ES.fromEnum ServerStarted
wordServerFailed  = ES.fromEnum ServerFailed
wordServerError   = ES.fromEnum ServerError
wordNoSuchClient  = ES.fromEnum NoSuchClient
wordLoadFailure   = ES.fromEnum LoadFailure
wordInitFailure   = ES.fromEnum InitFailure
wordShmFailure    = ES.fromEnum ShmFailure
wordVersionError  = ES.fromEnum VersionError


type PortFlagSet = ES.T C.CULong PortFlag

data PortFlag =
     PortIsInput
   | PortIsOutput
   | PortIsPhysical
   | PortCanMonitor
   | PortIsTerminal
     deriving (Enum, Eq, Ord, Show, Ix)


newtype PortName = PortName {deconsPortName :: CString}

portIsInput, portIsOutput :: PortFlagSet
portIsInput  = ES.fromEnum PortIsInput
portIsOutput = ES.fromEnum PortIsOutput


foreign import ccall "static jack/jack.h jack_port_register"
  port_register :: Ptr Client -> PortName -> CString ->
        PortFlagSet -> C.CULong -> IO (Ptr (Port a))


-- | represents absolute frame time
newtype NFrames = NFrames C.CUInt
    deriving (Show, Eq, Ord)

nframesToWord :: NFrames -> Word
nframesToWord (NFrames n) = fromIntegral n

instance Ix NFrames where
    range (a,b) =
        map (NFrames . fromIntegral) $
        range (nframesToWord a, nframesToWord b)
    index (a,b) i =
        index (nframesToWord a, nframesToWord b) (nframesToWord i)
    inRange (a,b) i =
        inRange (nframesToWord a, nframesToWord b) (nframesToWord i)
    rangeSize (a,b) =
        rangeSize (nframesToWord a, nframesToWord b)

instance Monoid NFrames where
    mempty = NFrames 0
    mappend (NFrames x) (NFrames y) = NFrames (x+y)


nframesIndices :: NFrames -> [NFrames]
nframesIndices (NFrames n) =
    take (fromIntegral n) $ map NFrames $ iterate (1+) 0

nframesBounds :: NFrames -> (NFrames,NFrames)
nframesBounds (NFrames n) =
    (NFrames 0, NFrames $ n - 1)


data CallbackArg = CallbackArg


type Process = NFrames -> Ptr CallbackArg -> IO E.Errno

foreign import ccall "static jack/jack.h jack_set_process_callback"
  set_process_callback ::
        Ptr Client -> FunPtr Process -> Ptr CallbackArg -> IO E.Errno

type ClientRegistration = CString -> C.CInt -> Ptr CallbackArg -> IO ()

foreign import ccall "static jack/jack.h jack_set_client_registration_callback"
  set_client_registration_callback ::
        Ptr Client -> FunPtr ClientRegistration -> Ptr CallbackArg -> IO E.Errno

newtype PortId = PortId Word32 deriving (Eq, Ord, Show)
{-# LINE 170 "src/Sound/JACK/FFI.hsc" #-}

{- |
Type argument for Jack ports where the type of samples transported by the port
is unknown.
-}
data UnknownType = UnknownType

type PortRegistration = PortId -> C.CInt -> Ptr CallbackArg -> IO ()

foreign import ccall "static jack/jack.h jack_set_port_registration_callback"
  set_port_registration_callback ::
        Ptr Client -> FunPtr PortRegistration -> Ptr CallbackArg -> IO E.Errno

type PortConnect = PortId -> PortId -> C.CInt -> Ptr CallbackArg -> IO ()

foreign import ccall "static jack/jack.h jack_set_port_connect_callback"
  set_port_connect_callback ::
        Ptr Client -> FunPtr PortConnect -> Ptr CallbackArg -> IO E.Errno


foreign import ccall "static jack/jack.h jack_port_by_id"
  port_by_id ::
        Ptr Client -> PortId -> IO (Ptr (Port UnknownType))

foreign import ccall "static jack/jack.h jack_port_by_name"
  port_by_name ::
        Ptr Client -> PortName -> IO (Ptr (Port UnknownType))

foreign import ccall "static jack/jack.h jack_port_name"
  port_name ::
        Ptr (Port typ) -> IO CString

foreign import ccall "static jack/jack.h jack_port_short_name"
  port_short_name ::
        Ptr (Port typ) -> IO CString

foreign import ccall "static jack/jack.h jack_port_flags"
  port_flags ::
        Ptr (Port typ) -> IO PortFlagSet

foreign import ccall "static jack/jack.h jack_port_type"
  port_type ::
        Ptr (Port UnknownType) -> IO PortName

foreign import ccall "static jack/jack.h jack_get_ports"
  get_ports :: Ptr Client -> CString -> CString -> C.CULong -> IO (Ptr CString)
--  get_ports :: Ptr Client -> CString -> CString -> C.CULong -> IO (Ptr PortName)

foreign import ccall "static jack/jack.h jack_port_get_all_connections"
  port_get_all_connections ::
        Ptr Client -> Ptr (Port typ) -> IO (Ptr CString)


foreign import ccall "static jack/jack.h jack_last_frame_time"
  last_frame_time :: Ptr Client -> IO NFrames

foreign import ccall "static jack/jack.h jack_port_get_buffer"
  port_get_buffer :: Ptr (Port a) -> NFrames -> IO (Ptr a)

foreign import ccall "static jack/jack.h jack_get_buffer_size"
  get_buffer_size :: Ptr Client -> IO (C.CUInt)

foreign import ccall "static jack/jack.h jack_activate"
  activate :: Ptr Client -> IO E.Errno

foreign import ccall "static jack/jack.h jack_client_close"
  client_close :: Ptr Client -> IO E.Errno

-- may return eEXIST
foreign import ccall "static jack/jack.h jack_connect"
  connect :: Ptr Client -> PortName -> PortName -> IO E.Errno

foreign import ccall "static jack/jack.h jack_disconnect"
  disconnect :: Ptr Client -> PortName -> PortName -> IO E.Errno

foreign import ccall "static jack/jack.h jack_port_unregister"
  port_unregister :: Ptr Client -> Ptr (Port a) -> IO E.Errno

foreign import ccall "static jack/jack.h jack_deactivate"
  deactivate :: Ptr Client -> IO E.Errno