module Data.Conduit.List
    ( 
      sourceList
    , sourceNull
    , unfold
    , enumFromTo
    , iterate
      
      
    , fold
    , foldMap
    , take
    , drop
    , head
    , peek
    , consume
    , sinkNull
      
    , foldM
    , mapM_
      
      
    , map
    , mapMaybe
    , catMaybes
    , concatMap
    , concatMapAccum
    , groupBy
    , isolate
    , filter
      
    , mapM
    , mapMaybeM
    , concatMapM
    , concatMapAccumM
      
    , sequence
    ) where
import qualified Prelude
import Prelude
    ( ($), return, (==), (), Int
    , (.), id, Maybe (..), Monad
    , Bool (..)
    , (>>)
    , (>>=)
    , seq
    , otherwise
    , Enum (succ), Eq
    , maybe
    , either
    )
import Data.Monoid (Monoid, mempty, mappend)
import Data.Conduit hiding (Source, Sink, Conduit, Pipe)
import Data.Conduit.Internal (sourceList)
import Control.Monad (when, (<=<))
import Control.Monad.Trans.Class (lift)
unfold :: Monad m
       => (b -> Maybe (a, b))
       -> b
       -> GSource m a
unfold f =
    go
  where
    go seed =
        case f seed of
            Just (a, seed') -> yield a >> go seed'
            Nothing -> return ()
enumFromTo :: (Enum a, Eq a, Monad m)
           => a
           -> a
           -> GSource m a
enumFromTo start stop =
    go start
  where
    go i
        | i == stop = yield i
        | otherwise = yield i >> go (succ i)
iterate :: Monad m => (a -> a) -> a -> GSource m a
iterate f =
    go
  where
    go a = yield a >> go (f a)
fold :: Monad m
     => (b -> a -> b)
     -> b
     -> GSink a m b
fold f =
    loop
  where
    loop accum =
        await >>= maybe (return accum) go
      where
        go a =
            let accum' = f accum a
             in accum' `seq` loop accum'
foldM :: Monad m
      => (b -> a -> m b)
      -> b
      -> GSink a m b
foldM f =
    loop
  where
    loop accum = do
        await >>= maybe (return accum) go
      where
        go a = do
            accum' <- lift $ f accum a
            accum' `seq` loop accum'
foldMap :: (Monad m, Monoid b)
        => (a -> b)
        -> GSink a m b
foldMap f =
    fold combiner mempty
  where
    combiner accum = mappend accum . f
mapM_ :: Monad m
      => (a -> m ())
      -> GInfSink a m
mapM_ f = awaitForever $ lift . f
drop :: Monad m
     => Int
     -> GSink a m ()
drop =
    loop
  where
    loop 0 = return ()
    loop count = await >>= maybe (return ()) (\_ -> loop (count  1))
take :: Monad m
     => Int
     -> GSink a m [a]
take =
    loop id
  where
    loop front 0 = return $ front []
    loop front count = await >>= maybe
        (return $ front [])
        (\x -> loop (front .(x:)) (count  1))
head :: Monad m => GSink a m (Maybe a)
head = await
peek :: Monad m => GLSink a m (Maybe a)
peek = await >>= maybe (return Nothing) (\x -> leftover x >> return (Just x))
map :: Monad m => (a -> b) -> GInfConduit a m b
map f = awaitForever $ yield . f
mapM :: Monad m => (a -> m b) -> GInfConduit a m b
mapM f = awaitForever $ yield <=< lift . f
mapMaybe :: Monad m => (a -> Maybe b) -> GInfConduit a m b
mapMaybe f = awaitForever $ maybe (return ()) yield . f
mapMaybeM :: Monad m => (a -> m (Maybe b)) -> GInfConduit a m b
mapMaybeM f = awaitForever $ maybe (return ()) yield <=< lift . f
catMaybes :: Monad m => GInfConduit (Maybe a) m a
catMaybes = awaitForever $ maybe (return ()) yield
concatMap :: Monad m => (a -> [b]) -> GInfConduit a m b
concatMap f = awaitForever $ sourceList . f
concatMapM :: Monad m => (a -> m [b]) -> GInfConduit a m b
concatMapM f = awaitForever $ sourceList <=< lift . f
concatMapAccum :: Monad m => (a -> accum -> (accum, [b])) -> accum -> GInfConduit a m b
concatMapAccum f =
    loop
  where
    loop accum =
        awaitE >>= either return go
      where
        go a = do
            let (accum', bs) = f a accum
            Prelude.mapM_ yield bs
            loop accum'
concatMapAccumM :: Monad m => (a -> accum -> m (accum, [b])) -> accum -> GInfConduit a m b
concatMapAccumM f =
    loop
  where
    loop accum = do
        awaitE >>= either return go
      where
        go a = do
            (accum', bs) <- lift $ f a accum
            Prelude.mapM_ yield bs
            loop accum'
consume :: Monad m => GSink a m [a]
consume =
    loop id
  where
    loop front = await >>= maybe (return $ front []) (\x -> loop $ front . (x:))
groupBy :: Monad m => (a -> a -> Bool) -> GInfConduit a m [a]
groupBy f =
    start
  where
    start = awaitE >>= either return (loop id)
    loop rest x =
        awaitE >>= either (\r -> yield (x : rest []) >> return r) go
      where
        go y
            | f x y     = loop (rest . (y:)) x
            | otherwise = yield (x : rest []) >> loop id y
isolate :: Monad m => Int -> GConduit a m a
isolate =
    loop
  where
    loop 0 = return ()
    loop count = await >>= maybe (return ()) (\x -> yield x >> loop (count  1))
filter :: Monad m => (a -> Bool) -> GInfConduit a m a
filter f = awaitForever $ \i -> when (f i) (yield i)
sinkNull :: Monad m => GInfSink a m
sinkNull = awaitForever $ \_ -> return ()
sourceNull :: Monad m => GSource m a
sourceNull = return ()
sequence :: Monad m
         => GLSink i m o 
         -> GLInfConduit i m o
sequence sink =
    self
  where
    self = awaitForever $ \i -> leftover i >> sink >>= yield