-- | Provides the possible ways to reduce a command sequence.
shrinkCommands :: [ShrinkOperation] -> Interaction -> [Interaction]
shrinkCommands sops tr =
    let op0 = filter (not.null) [ filter ((i/=) . fst) tr  |  i<-nub (map fst tr) ]
        op1 = filter (not . null) [ removeSend tr ] 
     in (if SrkRemoveInteraction `elem` sops then op0 else [])
        ++ (if SrkRemoveMessages `elem` sops then op1 else [])
  where
    removeSend tr = reverse$
        case break isSend (reverse tr) of
          (bfs,(_,(ps,Send c sid _)):rs) -> 
               filter (\cm -> not (isWaitRecv ps c sid cm) && not (isWaitSendCompletion ps c sid cm)) bfs ++ rs
          _ -> []

    isSend (_,(_,Send _ _ _)) = True
    isSend _                  = False

    isWaitSendCompletion ps c sid (_,(ps',WaitSendCompletion c' sid')) = c==c' && sid==sid' && not (null (intersect ps ps'))
    isWaitSendCompletion _ _ _ _ = False
    isWaitRecv ps c sid (_,(ps',WaitRecv c' sid')) = c==c' && sid==sid' && null (intersect ps ps')
    isWaitRecv _ _ _ _ = False


-- | Shrinks a command sequence as much as possible while preserving a faulty behavior
-- with respect to the provided predicate.
shrinkCommands :: [ShrinkOperation] -> Interaction -> [Interaction]
shrinkCommands sops tr =
    let op0 = filter (not.null) [ filter ((i/=) . fst) tr  |  i<-nub (map fst tr) ]
        op1 = filter (not . null) [ removeSend tr ] 
     in (if SrkRemoveInteraction `elem` sops then op0 else [])
        ++ (if SrkRemoveMessages `elem` sops then op1 else [])
  where
    removeSend tr = reverse$
        case break isSend (reverse tr) of
          (bfs,(_,(ps,Send c sid _)):rs) -> 
               filter (\cm -> not (isWaitRecv ps c sid cm) && not (isWaitSendCompletion ps c sid cm)) bfs ++ rs
          _ -> []

    isSend (_,(_,Send _ _ _)) = True
    isSend _                  = False

    isWaitSendCompletion ps c sid (_,(ps',WaitSendCompletion c' sid')) = c==c' && sid==sid' && not (null (intersect ps ps'))
    isWaitSendCompletion _ _ _ _ = False
    isWaitRecv ps c sid (_,(ps',WaitRecv c' sid')) = c==c' && sid==sid' && null (intersect ps ps')
    isWaitRecv _ _ _ _ = False


-- | Shrinks a command sequence as much as possible while preserving a faulty behavior
-- with respect to the provided predicate.
shrinkCommands sops tr =
    let op0 = filter (not.null) [ filter ((i/=) . fst) tr  |  i<-nub (map fst tr) ]
        op1 = filter (not . null) [ removeSend tr ] 
     in (if SrkRemoveInteraction `elem` sops then op0 else [])
        ++ (if SrkRemoveMessages `elem` sops then op1 else [])
  where
    removeSend tr = reverse$
        case break isSend (reverse tr) of
          (bfs,(_,(ps,Send c sid _)):rs) -> 
               filter (\cm -> not (isWaitRecv ps c sid cm) && not (isWaitSendCompletion ps c sid cm)) bfs ++ rs
          _ -> []

    isSend (_,(_,Send _ _ _)) = True
    isSend _                  = False

    isWaitSendCompletion ps c sid (_,(ps',WaitSendCompletion c' sid')) = c==c' && sid==sid' && not (null (intersect ps ps'))
    isWaitSendCompletion _ _ _ _ = False
    isWaitRecv ps c sid (_,(ps',WaitRecv c' sid')) = c==c' && sid==sid' && null (intersect ps ps')
    isWaitRecv _ _ _ _ = False


-- | Shrinks a command sequence as much as possible while preserving a faulty behavior
-- with respect to the provided predicate.