{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Control.Distributed.Process.Node.Lifted
   ( module Control.Distributed.Process.Node.Lifted
   , Base.LocalNode
   , Base.initRemoteTable
   , Base.localNodeId
   ) where


import Control.Monad.Base ( MonadBase, liftBase )
import Control.Monad (void)
import Control.Distributed.Process (ProcessId, Process, RemoteTable)
import Control.Distributed.Process.Node (LocalNode)

import Control.Exception (throw, SomeException)
import Control.Exception.Lifted (try)
import Network.Transport (Transport)
import qualified Control.Distributed.Process.Node as Base
import Control.Concurrent.MVar.Lifted
       (newEmptyMVar, putMVar, takeMVar)
import           Control.Distributed.Process.MonadBaseControl                     ()
import Control.DeepSeq (NFData, deepseq)


-- | Generalized version of 'MVar.putMVar'.
closeLocalNode ::  MonadBase IO m => LocalNode -> m ()
closeLocalNode = liftBase . Base.closeLocalNode

-- | Generalized version of 'Base.forkProcess'.
forkProcess :: MonadBase IO m => LocalNode -> Process () -> m ProcessId
forkProcess n = liftBase . Base.forkProcess n

-- | Generalized version of 'Base.newLocalNode'.
newLocalNode :: MonadBase IO m => Transport -> RemoteTable -> m LocalNode
newLocalNode t = liftBase . Base.newLocalNode t

-- | Generalized version of 'Base.runProcess'
runProcess :: MonadBase IO m => LocalNode -> Process () -> m ()
runProcess n = liftBase . Base.runProcess n

-- | A variant of 'runProcess' which returns a value. This works just
-- like 'runProcess' by forking a new process with a captured MVar, but it
-- will take the result of the computation. If the computation throws an
-- exception, it will be re-thrown by 'fromProcess' in the calling thread.
fromProcess :: forall a m. (NFData a, MonadBase IO m)
            => LocalNode -> Process a -> m a
fromProcess node ma =
 do resultMV <- newEmptyMVar
    void . forkProcess node $
     do eresult <- try (do a <- ma; a `deepseq` return a) :: Process (Either SomeException a)
        case eresult of
            Right result -> putMVar resultMV result
            Left exception -> putMVar resultMV (throw exception)
    !result <- takeMVar resultMV
    return result