    {- |
       Utility module, with various useful stuff.
    -}

    module Bio.Util (lines, splitWhen, countIO, sequence', mylines) where

    import Prelude hiding (lines)
    import System.IO        (stderr, hPutStr, hFlush)
    import System.IO.Unsafe (unsafeInterleaveIO)
    import Data.List        (groupBy)
    import qualified Data.ByteString.Lazy.Char8 as B

    -- | Workaround, the current "Data.ByteString.Lazy.Char8" contains a bug in 'Data.ByteString.Lazy.Char8.lines'.
    lines, mylines :: B.ByteString -> [B.ByteString]
    lines = case length (B.lines $ B.pack "\n") of 1 -> B.lines
                                                   0 -> mylines
                                                   _ -> error "Unknown implementaion of 'lines'!?"

    mylines s = case B.elemIndex '\n' s of
                  Nothing -> if B.null s then [] else [s]
                  Just i  -> B.take i s : mylines (B.drop (i+1) s)

    -- | Break a list of bytestrings on a predicate.
    splitWhen :: (B.ByteString -> Bool) -> [B.ByteString] -> [[B.ByteString]]
    splitWhen p = groupBy (\_ y -> B.null y || not (p y))

    -- | Output (to stderr) progress while evaluating a lazy list.
    --   Useful for generating output while (conceptually, at least) in pure code
    countIO :: String -> String -> Int -> [a] -> IO [a]
    countIO msg post step xs = sequence' $ map unsafeInterleaveIO ((blank >> outmsg (0::Int) >> c):cs)
       where (c:cs) = ct 0 xs
             output   = hPutStr stderr
             blank    = output ('\r':take 70 (repeat ' '))
             outmsg x = output ('\r':msg++show x) >> hFlush stderr
             ct s ys = let (a,b) = splitAt (step-1) ys
                           next  = s+step
                       in case b of [b1] -> map return a ++ [outmsg (s+step) >> hPutStr stderr post >> return b1]
                                    []   -> map return (init a) ++ [outmsg (s+length a) >> hPutStr stderr post >> return (last a)]
                                    _ -> map return a ++ [outmsg s >> return (head b)] ++ ct next (tail b)

    -- | A lazier version of 'Control.Monad.sequence' in "Control.Monad", needed by 'countIO' above.
    sequence' :: [IO a] -> IO [a]
    sequence' ms = foldr k (return []) ms
        where k m m' = do { x <- m; xs <- unsafeInterleaveIO m'; return (x:xs) }