module Data.Iteratee.IO.Handle(
  
  enumHandle
  ,enumHandleCatch
  ,enumHandleRandom
  ,enumFile
  ,enumFileRandom
  
  ,fileDriverHandle
  ,fileDriverRandomHandle
)
where
import Data.Iteratee.Base.ReadableChunk
import Data.Iteratee.Iteratee
import Data.Iteratee.Binary()
import Control.Exception
import Control.Monad
import Control.Monad.CatchIO as CIO
import Control.Monad.IO.Class
import Foreign.Ptr
import Foreign.Storable
import Foreign.Marshal.Alloc
import System.IO
makeHandleCallback ::
  (MonadCatchIO m, NullPoint s, ReadableChunk s el) =>
  Ptr el
  -> Int
  -> Handle
  -> st
  -> m (Either SomeException ((Bool, st), s))
makeHandleCallback p bsize h st = do
  n' <- liftIO (CIO.try $ hGetBuf h p bsize :: IO (Either SomeException Int))
  case n' of
    Left e -> return $ Left e
    Right 0 -> return $ Right ((False, st), empty)
    Right n -> liftM (\s -> Right ((True, st), s)) $
                 readFromPtr p (fromIntegral n)
enumHandle ::
 forall s el m a.(NullPoint s, ReadableChunk s el, MonadCatchIO m) =>
  Int 
  -> Handle
  -> Enumerator s m a
enumHandle bs h i =
  let bufsize = bs * sizeOf (undefined :: el)
  in CIO.bracket (liftIO $ mallocBytes bufsize)
                 (liftIO . free)
                 (\p -> enumFromCallback (makeHandleCallback p bufsize h) () i)
enumHandleCatch
 ::
 forall e s el m a.(IException e,
                    NullPoint s,
                    ReadableChunk s el,
                    MonadCatchIO m)
  => Int 
  -> Handle
  -> (e -> m (Maybe EnumException))
  -> Enumerator s m a
enumHandleCatch bs h handler i =
  let bufsize = bs * sizeOf (undefined :: el)
  in CIO.bracket (liftIO $ mallocBytes bufsize)
                 (liftIO . free)
                 (\p -> enumFromCallbackCatch (makeHandleCallback p bufsize h) handler () i)
enumHandleRandom ::
 forall s el m a.(NullPoint s, ReadableChunk s el, MonadCatchIO m) =>
  Int 
  -> Handle
  -> Enumerator s m a
enumHandleRandom bs h i = enumHandleCatch bs h handler i
  where
    handler (SeekException off) =
       liftM (either
              (Just . EnumException :: IOException -> Maybe EnumException)
              (const Nothing))
             . liftIO . CIO.try $ hSeek h AbsoluteSeek $ fromIntegral off
enumFile' :: (NullPoint s, MonadCatchIO m, ReadableChunk s el) =>
  (Int -> Handle -> Enumerator s m a)
  -> Int 
  -> FilePath
  -> Enumerator s m a
enumFile' enumf bufsize filepath iter = CIO.bracket
  (liftIO $ openBinaryFile filepath ReadMode)
  (liftIO . hClose)
  (flip (enumf bufsize) iter)
enumFile ::
  (NullPoint s, MonadCatchIO m, ReadableChunk s el)
  => Int                 
  -> FilePath
  -> Enumerator s m a
enumFile = enumFile' enumHandle
enumFileRandom ::
  (NullPoint s, MonadCatchIO m, ReadableChunk s el)
  => Int                 
  -> FilePath
  -> Enumerator s m a
enumFileRandom = enumFile' enumHandleRandom
fileDriverHandle
  :: (NullPoint s, MonadCatchIO m, ReadableChunk s el) =>
     Int                      
     -> Iteratee s m a
     -> FilePath
     -> m a
fileDriverHandle bufsize iter filepath =
  enumFile bufsize filepath iter >>= run
fileDriverRandomHandle
  :: (NullPoint s, MonadCatchIO m, ReadableChunk s el) =>
     Int                      
     -> Iteratee s m a
     -> FilePath
     -> m a
fileDriverRandomHandle bufsize iter filepath =
  enumFileRandom bufsize filepath iter >>= run