-- | Takes two processes identifiers and generates an interaction between them.
--
-- Right now the interactions consist on stablishing an initial connection and 
-- then having the processes send messages to each other.
--
genInteraction :: TestConfig -> Int -> Int -> CommandGen Interaction 
genInteraction c p0 p1 = do
    i <- generateInteractionId
    mt <- getRandomTimeout
    cid <- generateConnectionId
    sends <- genSends [] [] cid p0 p1 1
    cmds <- if testRMA c then do
                 rmaOps <- genRMAInteraction cid p0 p1
                 mergeI sends rmaOps
             else return sends
    return$ (i,([p1],Accept cid)):
       (zip (repeat i) ( ([p0],ConnectTo "" p1 cid mt) : ([p0,p1],WaitConnection cid) : cmds ++ [ ([p0,p1],Disconnect cid) ]
                         )) -- ++ [([p0],WaitEvents (nSends c+1)),([p1],WaitEvents (nSends c+2))] ))
  where
    getRandomTimeout = do
        b <- getRandom
        if b then return Nothing
          else return Nothing -- fmap (Just . (+6*1000000))$ getRandom

    genSends :: [ProcCommand] -> [ProcCommand] -> WordPtr -> Int -> Int -> Int -> CommandGen [ProcCommand]
    genSends waits0 waits _cid _p0 _p1 mid | mid-1 >= nSends c = return$ reverse waits0 ++ reverse waits
               
    genSends waits0 waits1 cid p0 p1 mid = do
        insertWaits0 <- getRandom 
        insertWaits1 <- getRandom 
        msgLen <- getRandomR (nMinMsgLen c,nMaxMsgLen c) 
        direction <- getRandom
        let (p0',p1') = if direction then (p0,p1) else (p1,p0)

            waits0' = ([p0'],WaitSendCompletion cid (fromIntegral mid)) : if insertWaits0 then [] else waits0
            waits1' = ([p1'],WaitRecv cid (fromIntegral mid)) : if insertWaits1 then [] else waits1
            waits0'' = if insertWaits0 then reverse waits0 else []
            waits1'' = if insertWaits1 then reverse waits1 else []
        rest <- genSends waits0' waits1' cid p0' p1' (mid+1)
        return$ waits0'' ++ waits1'' ++ ([p0'],Send cid (fromIntegral mid) (Msg (fromIntegral mid) msgLen)) : rest

    genRMAInteraction :: WordPtr -> Int -> Int -> CommandGen [ProcCommand]
    genRMAInteraction cid p0 p1 = do
        b0 <- getRandom
        b1 <- getRandom
        let maybeReuse = (if b0 then (([p0],RMAReuseRMAHandle cid):) else id) 
                       . (if b1 then (([p1],RMAReuseRMAHandle cid):) else id)
        sends <- genRMAMsgs [] cid p0 p1 (nSends c+1)
        let rmaSendId = fromIntegral (3*nSends c)+cid+1
        return$ maybeReuse$ ([p0],RMAHandleExchange cid rmaSendId) 
                          : ([p1],RMAHandleExchange cid rmaSendId) : ([p0,p1],RMAWaitExchange cid) 
                          : sends
                          ++ ([p0],RMAFreeHandles cid) : ([p1],RMAFreeHandles cid) : []

    genRMAMsgs :: [ProcCommand] -> WordPtr -> Int -> Int -> Int -> CommandGen [ProcCommand]
    genRMAMsgs waits _cid _p0 _p1 mid | mid-1 >= (2*nSends c) = return$ reverse waits
               
    genRMAMsgs waits cid p0 p1 mid = do
        genWrite <- getRandom
        insertWaits <- return True -- getRandom 
        direction <- getRandom
        let (p0',p1') = if direction then (p0,p1) else (p1,p0)

            waits' = ([p0',p1'],(if genWrite then RMAWaitWrite
                                 else RMAWaitRead) cid (fromIntegral mid))
                       : if insertWaits then [] else waits
            waits1 = if insertWaits then reverse waits else []
        rest <- genRMAMsgs waits' cid p0' p1' (mid+1)
        let sendCmd = if genWrite then RMAWrite else RMARead
            prepareRead = if genWrite then [] else [([p1'],RMAPrepareRead cid (fromIntegral mid))]
        return$ waits1 ++ prepareRead ++ ([p0'],sendCmd cid (fromIntegral mid)) : rest


-- | A monad for processes. It carries along a state with the file handles
-- used to comunicate with each process. It collects, in addition, the
-- responses provided by such processes.
--
genInteraction :: TestConfig -> Int -> Int -> CommandGen Interaction
genInteraction c p0 p1 = do
    i <- generateInteractionId
    mt <- getRandomTimeout
    cid <- generateConnectionId
    sends <- genSends [] [] cid p0 p1 1
    cmds <- if testRMA c then do
                 rmaOps <- genRMAInteraction cid p0 p1
                 mergeI sends rmaOps
             else return sends
    return$ (i,([p1],Accept cid)):
       (zip (repeat i) ( ([p0],ConnectTo "" p1 cid mt) : ([p0,p1],WaitConnection cid) : cmds ++ [ ([p0,p1],Disconnect cid) ]
                         )) -- ++ [([p0],WaitEvents (nSends c+1)),([p1],WaitEvents (nSends c+2))] ))
  where
    getRandomTimeout = do
        b <- getRandom
        if b then return Nothing
          else return Nothing -- fmap (Just . (+6*1000000))$ getRandom

    genSends :: [ProcCommand] -> [ProcCommand] -> WordPtr -> Int -> Int -> Int -> CommandGen [ProcCommand]
    genSends waits0 waits _cid _p0 _p1 mid | mid-1 >= nSends c = return$ reverse waits0 ++ reverse waits
               
    genSends waits0 waits1 cid p0 p1 mid = do
        insertWaits0 <- getRandom 
        insertWaits1 <- getRandom 
        msgLen <- getRandomR (nMinMsgLen c,nMaxMsgLen c) 
        direction <- getRandom
        let (p0',p1') = if direction then (p0,p1) else (p1,p0)

            waits0' = ([p0'],WaitSendCompletion cid (fromIntegral mid)) : if insertWaits0 then [] else waits0
            waits1' = ([p1'],WaitRecv cid (fromIntegral mid)) : if insertWaits1 then [] else waits1
            waits0'' = if insertWaits0 then reverse waits0 else []
            waits1'' = if insertWaits1 then reverse waits1 else []
        rest <- genSends waits0' waits1' cid p0' p1' (mid+1)
        return$ waits0'' ++ waits1'' ++ ([p0'],Send cid (fromIntegral mid) (Msg (fromIntegral mid) msgLen)) : rest

    genRMAInteraction :: WordPtr -> Int -> Int -> CommandGen [ProcCommand]
    genRMAInteraction cid p0 p1 = do
        b0 <- getRandom
        b1 <- getRandom
        let maybeReuse = (if b0 then (([p0],RMAReuseRMAHandle cid):) else id) 
                       . (if b1 then (([p1],RMAReuseRMAHandle cid):) else id)
        sends <- genRMAMsgs [] cid p0 p1 (nSends c+1)
        let rmaSendId = fromIntegral (3*nSends c)+cid+1
        return$ maybeReuse$ ([p0],RMAHandleExchange cid rmaSendId) 
                          : ([p1],RMAHandleExchange cid rmaSendId) : ([p0,p1],RMAWaitExchange cid) 
                          : sends
                          ++ ([p0],RMAFreeHandles cid) : ([p1],RMAFreeHandles cid) : []

    genRMAMsgs :: [ProcCommand] -> WordPtr -> Int -> Int -> Int -> CommandGen [ProcCommand]
    genRMAMsgs waits _cid _p0 _p1 mid | mid-1 >= (2*nSends c) = return$ reverse waits
               
    genRMAMsgs waits cid p0 p1 mid = do
        genWrite <- getRandom
        insertWaits <- return True -- getRandom 
        direction <- getRandom
        let (p0',p1') = if direction then (p0,p1) else (p1,p0)

            waits' = ([p0',p1'],(if genWrite then RMAWaitWrite
                                 else RMAWaitRead) cid (fromIntegral mid))
                       : if insertWaits then [] else waits
            waits1 = if insertWaits then reverse waits else []
        rest <- genRMAMsgs waits' cid p0' p1' (mid+1)
        let sendCmd = if genWrite then RMAWrite else RMARead
            prepareRead = if genWrite then [] else [([p1'],RMAPrepareRead cid (fromIntegral mid))]
        return$ waits1 ++ prepareRead ++ ([p0'],sendCmd cid (fromIntegral mid)) : rest


-- | A monad for processes. It carries along a state with the file handles
-- used to comunicate with each process. It collects, in addition, the
-- responses provided by such processes.
--
genInteraction c p0 p1 = do
    i <- generateInteractionId
    mt <- getRandomTimeout
    cid <- generateConnectionId
    sends <- genSends [] [] cid p0 p1 1
    cmds <- if testRMA c then do
                 rmaOps <- genRMAInteraction cid p0 p1
                 mergeI sends rmaOps
             else return sends
    return$ (i,([p1],Accept cid)):
       (zip (repeat i) ( ([p0],ConnectTo "" p1 cid mt) : ([p0,p1],WaitConnection cid) : cmds ++ [ ([p0,p1],Disconnect cid) ]
                         )) -- ++ [([p0],WaitEvents (nSends c+1)),([p1],WaitEvents (nSends c+2))] ))
  where
    getRandomTimeout = do
        b <- getRandom
        if b then return Nothing
          else return Nothing -- fmap (Just . (+6*1000000))$ getRandom

    genSends :: [ProcCommand] -> [ProcCommand] -> WordPtr -> Int -> Int -> Int -> CommandGen [ProcCommand]
    genSends waits0 waits _cid _p0 _p1 mid | mid-1 >= nSends c = return$ reverse waits0 ++ reverse waits
               
    genSends waits0 waits1 cid p0 p1 mid = do
        insertWaits0 <- getRandom 
        insertWaits1 <- getRandom 
        msgLen <- getRandomR (nMinMsgLen c,nMaxMsgLen c) 
        direction <- getRandom
        let (p0',p1') = if direction then (p0,p1) else (p1,p0)

            waits0' = ([p0'],WaitSendCompletion cid (fromIntegral mid)) : if insertWaits0 then [] else waits0
            waits1' = ([p1'],WaitRecv cid (fromIntegral mid)) : if insertWaits1 then [] else waits1
            waits0'' = if insertWaits0 then reverse waits0 else []
            waits1'' = if insertWaits1 then reverse waits1 else []
        rest <- genSends waits0' waits1' cid p0' p1' (mid+1)
        return$ waits0'' ++ waits1'' ++ ([p0'],Send cid (fromIntegral mid) (Msg (fromIntegral mid) msgLen)) : rest

    genRMAInteraction :: WordPtr -> Int -> Int -> CommandGen [ProcCommand]
    genRMAInteraction cid p0 p1 = do
        b0 <- getRandom
        b1 <- getRandom
        let maybeReuse = (if b0 then (([p0],RMAReuseRMAHandle cid):) else id) 
                       . (if b1 then (([p1],RMAReuseRMAHandle cid):) else id)
        sends <- genRMAMsgs [] cid p0 p1 (nSends c+1)
        let rmaSendId = fromIntegral (3*nSends c)+cid+1
        return$ maybeReuse$ ([p0],RMAHandleExchange cid rmaSendId) 
                          : ([p1],RMAHandleExchange cid rmaSendId) : ([p0,p1],RMAWaitExchange cid) 
                          : sends
                          ++ ([p0],RMAFreeHandles cid) : ([p1],RMAFreeHandles cid) : []

    genRMAMsgs :: [ProcCommand] -> WordPtr -> Int -> Int -> Int -> CommandGen [ProcCommand]
    genRMAMsgs waits _cid _p0 _p1 mid | mid-1 >= (2*nSends c) = return$ reverse waits
               
    genRMAMsgs waits cid p0 p1 mid = do
        genWrite <- getRandom
        insertWaits <- return True -- getRandom 
        direction <- getRandom
        let (p0',p1') = if direction then (p0,p1) else (p1,p0)

            waits' = ([p0',p1'],(if genWrite then RMAWaitWrite
                                 else RMAWaitRead) cid (fromIntegral mid))
                       : if insertWaits then [] else waits
            waits1 = if insertWaits then reverse waits else []
        rest <- genRMAMsgs waits' cid p0' p1' (mid+1)
        let sendCmd = if genWrite then RMAWrite else RMARead
            prepareRead = if genWrite then [] else [([p1'],RMAPrepareRead cid (fromIntegral mid))]
        return$ waits1 ++ prepareRead ++ ([p0'],sendCmd cid (fromIntegral mid)) : rest


-- | A monad for processes. It carries along a state with the file handles
-- used to comunicate with each process. It collects, in addition, the
-- responses provided by such processes.
--