content: -- | A @VividAction m => m a@ can either be run immediately, be scheduled to run at a
  --   precise future time, or be used for non-realtime synthesis.
  -- 
  --   Note that at the moment VividAction has MonadIO, but this won't be true in
  --   upcoming versions (as early as the next release) - so don't get used
  --   to it!
  
  {-# LANGUAGE KindSignatures #-}
  {-# LANGUAGE Rank2Types #-}
  
  {-# LANGUAGE NoIncoherentInstances #-}
  {-# LANGUAGE NoMonomorphismRestriction #-}
  {-# LANGUAGE NoUndecidableInstances #-}
  
  module Vivid.Actions.Class (
       VividAction(..)
     -- , VA
     , callOSCAndSync
     , oscWSync
     ) where
  
  import Vivid.SC.Server.Types (BufferId, NodeId, SyncId(..))
  import qualified Vivid.SC.Server.Commands as SCCmd
  
  import Vivid.OSC
  import Vivid.SynthDef.Types (SynthDef)
  
  import Control.Monad.IO.Class (MonadIO)
  import Data.ByteString (ByteString)
  import qualified Data.ByteString.UTF8 as UTF8
  import Data.Int
  
  class (Monad m , MonadIO m) => VividAction (m :: * -> *) where
  
     -- | Send an 'OSC' message to the SuperCollider server
     callOSC :: OSC -> m ()
     callOSC = callBS . encodeOSC
  
     -- | Send a ByteString to the SuperCollider server.
     --   You usually want to use 'call' instead.
     callBS :: ByteString -> m ()
  
     -- | Blocks until the server finishes processing commands
     sync :: m ()
  
     -- | As the user, you probably don't want to use this:
     -- 
     --   Many commands already include a \"sync\" -- e.g.
     --   'makeBuffer' already syncs.
     -- 
     --   When you do want to do an
     --   explicit sync you probably want to use 'sync' instead, or
     --   'callOSCAndSync'
     waitForSync :: SyncId -> m ()
  
     -- | Wait, in seconds
     wait :: Real n => n -> m ()
  
     getTime :: m Timestamp
  
     newBufferId :: m BufferId
  
     newNodeId :: m NodeId
  
     newSyncId :: m SyncId
  
     fork :: m () -> m ()
  
     -- | Send a synth definition to be loaded on the SC server
     -- 
     --   Note that this is sort of optional -- if you don't call it, it'll be called the first time
     --   you call 'synth' with the SynthDef
     defineSD :: SynthDef a -> m ()
  
  -- | Send an OSC message and wait for it to complete before returning
  callOSCAndSync :: VividAction m => OSC -> m ()
  callOSCAndSync message = do
     now <- getTime
     syncId <- newSyncId
     callBS $ encodeOSCBundle $
        OSCBundle now [Right message, Right $ SCCmd.sync syncId]
     waitForSync syncId
  
  -- | 
  -- 
  --   Maybe can dedupe with 'callOSCAndSync'
  oscWSync :: VividAction m => (SyncId -> m ()) -> m ()
  oscWSync actionFromId = do
     syncId <- newSyncId
     actionFromId syncId
     waitForSync syncId
  
  
  -- type VA x = forall m. VividAction m => m x
  
  