{-# OPTIONS -XCPP #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  Control.Parallel.Eden.Edi
-- Copyright   :  (c) Philipps Universitaet Marburg 2005-2010
-- License     :  BSD-style (see the file LICENSE)
-- 
-- Maintainer  :  eden@mathematik.uni-marburg.de
-- Stability   :  beta
-- Portability :  not portable
--
-- The low-level parallel functional language:
--      EDen Implementation language
--
-- This module defines a thin layer of type-checking wrappers
-- over the parallel primitives implemented in ParPrim.hs.
--
-- Depends on the Eden Compiler.
--
-- Eden Group Marburg
--

module Control.Parallel.Eden.Edi 
-- interface:
   (fork,           -- :: IO () -> IO (), from conc.hs, without ThreadID
    spawnProcessAt, -- :: Int -> IO () -> IO ()
    spawnArgsProcessAt, -- ::NFData a =>  Int -> (a -> IO ()) -> a -> IO ()
    ChanName',      -- EdI channel type 
    createC,        -- :: IO (ChanName' a,a) , prim.Op.
    createCs,       -- :: Int -> IO ([ChanName' a],[a])
    sendWith,       -- :: (Strategy a) -> ChanName' a -> a -> IO ()
    sendNF,         -- :: NFData a => ChanName' a -> a -> IO ()
    sendStreamWith, -- :: (Strategy a) -> ChanName' [a] -> [a] -> IO ()
    sendNFStream,   -- :: NFData a => ChanName' [a] -> [a] -> IO ()
    noPe, selfPe,   -- :: IO Int
    NFData(..), -- reexported from Control.Deepseq
    using, r0, rseq, rdeepseq, seqList, seqFoldable 
                -- reexported from Control.Seq (sequential strategies) 
                -- selection rationale: same export as Eden module
   )
   where

import Control.Concurrent

#if defined(NOT_PARALLEL)
import Control.Parallel.Eden.ParPrimConcHs as ParPrim
#else
import Control.Parallel.Eden.ParPrim as ParPrim
#endif

import Control.DeepSeq(NFData(..))
import Control.Seq (Strategy, using, 
                       r0, rseq, rdeepseq, seqList, seqFoldable)

-- Helper function: Despite its name, seq does not guarantee sequence! We
-- specialise on strategy application (unit type) and use a trivial comparison
-- case.
pseq :: () -> b -> b -- strategy application -> b -> b
pseq strat_x y = if strat_x == () then y else error "Impossible case!"
infixr 0 `pseq`
-- We could import this from Control.Parallel.Strategies, but want to 
-- decouple the code from that module


-- Process Creation:
--------------------
spawnProcessAt :: Int -> IO () -> IO () -- forces IO() type!
spawnProcessAt pe action = sendData (Instantiate pe) action

-- additional: force evaluation of arguments (uncurried version)
spawnArgsProcessAt :: NFData a => Int -> (a -> IO()) -> a -> IO ()
spawnArgsProcessAt pe argsAction args 
               = (rnf args `seq` 
		  sendData (Instantiate pe) (argsAction args))

-- Communication:
-----------------

instance NFData (ChanName' a) -- defaulting to rwhnf.  
-- Can only be created by ParPrim* operations, so, fine with this default.


-- creation of n channels in one call, "safe" evaluation
createCs :: Int -> IO ([ChanName' a],[a])
createCs n | n >= 0 = do list <- sequence (replicate n createC)
			 let (cs, vs) = unzip list
                         rnf cs `pseq` -- channels fully evaluated
                         -- spine vs `seq` -- value list spine (optional)
                            return (cs,vs)
           | otherwise = error "createCs: n < 0"
             

-- Evaluation / Communication:
------------------------------
sendWith :: Strategy a -> ChanName' a -> a -> IO ()
--  Strategy a => a -> ()
sendWith strat c d = connectToPort c >> 
                     (strat d `pseq` sendData Data d)

-- sendChan with evaluation, without Connect message
sendNF :: NFData a => ChanName' a -> a -> IO ()
sendNF = sendWith rnf

sendStreamWith :: (a -> ()) -> ChanName' [a] -> [a] -> IO ()
--  Strategy a => a -> ()
sendStreamWith strat c xs = connectToPort c >> 
                            send xs
    where send l@[]   = sendData Data l
          send (x:xs) = strat x `pseq` sendData Stream x >>
                        send xs

sendNFStream :: NFData a => ChanName' [a] -> [a] -> IO ()
sendNFStream = sendStreamWith rnf

--------------------------------------------------------------

-- JUNKYARD:

-- monadic evaluation control
rnfM :: NFData a => a -> IO ()
rnfM x = case rnf x of { () -> return () } -- works as well
-- rnfM x = rnf x `pseq` return ()
-- rnfM  = return . rnf -- doznwork: returns _unevaluated_ (rnf x)

-- send without evaluation or Connect message
sendVia :: ChanName' a -> a -> IO ()
sendVia c d = connectToPort c >> 
	      sendData Data d
		      
-- send with NF evaluation and Connect message
connectSendNFvia :: NFData a => ChanName' a -> a -> IO ()
connectSendNFvia c d = connectToPort c >> 
		       sendData Connect d >>
		       rnfM d >>
		       sendData Data d

-- sendStream: Connect message followed by element-wise NF evaluation/send 
sendStreamNFvia :: NFData a => ChanName' [a] -> [a] -> IO ()
sendStreamNFvia c d = connectToPort c >> 
		      sendData Connect d >>
		      sendStream' d
    where sendStream'  l@[] = sendData Data l
	  sendStream' (x:xs)= rnfM x >>
			      sendData Stream x >> 
			      sendStream' xs