--------------------------------------------------------------------------------
-- |
-- Module    : Sound.ALSA.Sequencer.Subscribe
-- Copyright : (c) Dylan Simon, 2011
-- License   : BSD3
--
-- Stability : provisional
--
-- This module contains functions for working with subscriptions.
-- Reference:
-- <http://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_subscribe.html>
--------------------------------------------------------------------------------

module Sound.ALSA.Sequencer.Subscribe
  ( {- T

  , alloc
  , copy
  , clone

  , getSender
  , getDest
  , getQueue
  , getExclusive
  , getTimeUpdate
  , getTimeReal
  
  , setSender
  , setDest
  , setQueue
  , setExclusive
  , setTimeUpdate
  , setTimeReal

  , subscribePort
  , unsubscribePort

  , create
  , -} subscribe
  , unsubscribe
  ) where

import qualified Sound.ALSA.Sequencer.Marshal.Sequencer as Seq
import qualified Sound.ALSA.Sequencer.Area as Area
import qualified Sound.ALSA.Sequencer.Marshal.Address as Addr
import qualified Sound.ALSA.Sequencer.Marshal.Queue as Queue
import qualified Sound.ALSA.Exception as Exc

import Foreign.C.Types (CInt, )
import Foreign.Ptr (Ptr, )
import Foreign.Marshal.Alloc (alloca, )
import Foreign.Storable (peek, poke, )

type T = Area.PortSubscribe
type T_ = Area.PortSubscribe_

alloc	      :: IO T
_copy	      :: T -> T -> IO ()
_clone	      :: T -> IO T
_getQueue     :: T -> IO Queue.T
_getExclusive :: T -> IO Bool
_getTimeUpdate:: T -> IO Bool
_getTimeReal  :: T -> IO Bool
setQueue     :: T -> Queue.T -> IO ()
setExclusive  :: T -> Bool -> IO ()
setTimeUpdate :: T -> Bool -> IO ()
setTimeReal   :: T -> Bool -> IO ()

alloc	      = Area.port_subscribe_malloc
_copy	      = Area.port_subscribe_copy
_clone	      = Area.port_subscribe_clone
_getQueue     = Area.port_subscribe_get_queue
_getExclusive = Area.port_subscribe_get_exclusive
_getTimeUpdate= Area.port_subscribe_get_time_update
_getTimeReal  = Area.port_subscribe_get_time_real
setQueue      = Area.port_subscribe_set_queue
setExclusive  = Area.port_subscribe_set_exclusive
setTimeUpdate = Area.port_subscribe_set_time_update
setTimeReal   = Area.port_subscribe_set_time_real

-- | Get sender address of a port subscription
_getSender :: T -> IO Addr.T
_getSender s = 
  peek =<< Area.with_port_subscribe s snd_seq_port_subscribe_get_sender

foreign import ccall unsafe "alsa/asoundlib.h snd_seq_port_subscribe_get_sender"
  snd_seq_port_subscribe_get_sender :: Ptr T_ -> IO (Ptr Addr.T)

-- | Get destination address of a port subscription
_getDest :: T -> IO Addr.T
_getDest s = 
  peek =<< Area.with_port_subscribe s snd_seq_port_subscribe_get_dest

foreign import ccall unsafe "alsa/asoundlib.h snd_seq_port_subscribe_get_dest"
  snd_seq_port_subscribe_get_dest :: Ptr T_ -> IO (Ptr Addr.T)

-- | Set sender address of a port subscription
setSender :: T -> Addr.T -> IO ()
setSender s c =
  alloca $ \p -> poke p c >> Area.with_port_subscribe s (`snd_seq_port_subscribe_set_sender` p)

foreign import ccall unsafe "alsa/asoundlib.h snd_seq_port_subscribe_set_sender"
  snd_seq_port_subscribe_set_sender :: Ptr T_ -> Ptr Addr.T -> IO ()

-- | Set destination address of a port subscription
setDest :: T -> Addr.T -> IO ()
setDest s c =
  alloca $ \p -> poke p c >> Area.with_port_subscribe s (`snd_seq_port_subscribe_set_dest` p)

foreign import ccall unsafe "alsa/asoundlib.h snd_seq_port_subscribe_set_dest"
  snd_seq_port_subscribe_set_dest :: Ptr T_ -> Ptr Addr.T -> IO ()

-- | Subscribe a port connection
subscribePort :: Seq.T mode -> T -> IO ()
subscribePort (Seq.Cons h) s =
  Exc.checkResult_ "subscribe_port" =<< Area.with_port_subscribe s (snd_seq_subscribe_port h)

foreign import ccall unsafe "alsa/asoundlib.h snd_seq_subscribe_port"
  snd_seq_subscribe_port :: Ptr Seq.Core -> Ptr T_ -> IO CInt

-- | Unsubscribe a connection between ports
unsubscribePort :: Seq.T mode -> T -> IO ()
unsubscribePort (Seq.Cons h) s =
  Exc.checkResult_ "unsubscribe_port" =<< Area.with_port_subscribe s (snd_seq_unsubscribe_port h)

foreign import ccall unsafe "alsa/asoundlib.h snd_seq_unsubscribe_port"
  snd_seq_unsubscribe_port :: Ptr Seq.Core -> Ptr T_ -> IO CInt

create :: Addr.T -> Addr.T -> Bool -> Maybe (Queue.T, Bool) -> IO T
create sender dest excl time = do
  s <- alloc
  setSender s sender
  setDest s dest
  setExclusive s excl
  maybe (return ()) (\(queue, realtime) -> do
    setTimeUpdate s True
    setQueue s queue
    setTimeReal s realtime) time
  return s

-- | Subscribe a port connection: @'subscribeSimple' sender dest exclusive (Just (updatequeue, realtime))@
subscribe :: Seq.T mode -> Addr.T -> Addr.T -> Bool -> Maybe (Queue.T, Bool) -> IO ()
subscribe ss sender dest excl time = subscribePort ss =<< create sender dest excl time

-- | Unsubscribe a port connection: @'unsubscribeSimple' sender dest@
unsubscribe :: Seq.T mode -> Addr.T -> Addr.T -> IO ()
unsubscribe ss sender dest = unsubscribePort ss =<< create sender dest False Nothing