module Data.SouSiT.Handle (
    -- * Source
    hSource,
    hSource',
    hSourceRes,
    hSourceNoEOF,
    hSourceNoEOF',
    hSourceResNoEOF,
    -- * Sink
    hSink,
    hSinkRes
) where

import Data.SouSiT.Source
import Data.SouSiT.Sink
import System.IO
import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.Trans.Resource


-- | Source from a handle. The handle will not be closed and is read till hIsEOF.
hSource :: MonadIO m => (Handle -> m a) -> Handle -> FeedSource m a
hSource get = actionSource . toEof get

-- | Same as hSource, but opens the handle when transfer is called and closes it when
--   transfer/feedToSink completes.
--   Uses 'bracket' to ensure safe release of the allocated resources.
hSource' :: (Handle -> IO a) -> IO Handle -> FeedSource IO a
hSource' get open = bracketActionSource open (liftIO . hClose) (toEof get)

-- | Same as hSource, but opens the handle when transfer is called and closes it when
--   transfer/feedToSink completes.
hSourceRes :: (MonadIO m, MonadResource m) => (Handle -> m a) -> IO Handle -> FeedSource m a
hSourceRes get open = FeedSource fun
    where fun sink = do (r,h) <- allocate open (liftIO . hClose)
                        sink' <- feedToSink (actionSource $ toEof get h) sink
                        release r
                        return sink'

toEof get h = (liftIO . hIsEOF) h >>= next
    where next True  = return Nothing
          next False = liftM Just (get h)


-- | Same as hSource, but does not check for hIsEOF and therefore never terminates.
hSourceNoEOF :: MonadIO m => (Handle -> m a) -> Handle -> FeedSource m a
hSourceNoEOF get = actionSource . liftM Just . get

-- | Same as hSource', but does not check for hIsEOF and therefore never terminates.
hSourceNoEOF' :: (Handle -> IO a) -> IO Handle -> FeedSource IO a
hSourceNoEOF' get open = bracketActionSource open hClose (liftM Just . get)

-- | Same as hSourceRes', but does not check for hIsEOF and therefore never terminates.
hSourceResNoEOF :: (MonadIO m, MonadResource m) => (Handle -> m a) -> IO Handle -> FeedSource m a
hSourceResNoEOF get open = FeedSource fun
    where fun sink = do (r,h) <- allocate open (liftIO . hClose)
                        sink' <- feedToSink (actionSource $ liftM Just $ get h) sink
                        release r
                        return sink'


-- | Sink backed by a handle. The data will be written by the provided function.
--   The sink will never change to the SinkDone state (if the device is full then
--   the operation will simply fail).
--   The handle is not closed and exceptions are not catched.
hSink :: MonadIO m => (Handle -> a -> m ()) -> Handle -> Sink a m ()
hSink put h = actionSink (put h)

-- | Same as hSink, but does opens the handle when the first item is written.
--   The handle will be closed when the sink is closed.
hSinkRes :: (MonadIO m, MonadResource m) => (Handle -> a -> m ()) -> IO Handle -> Sink a m ()
hSinkRes put open = openCloseActionSink o (release . fst) (put . snd)
    where o = allocate open (liftIO . hClose)