module Network.Monad.Transfer.Offline where
import qualified Network.Monad.Transfer as Transfer
import qualified Network.Monad.Reader as Reader
import qualified Network.Monad.Body as Body
import qualified Network.Stream as Stream
import Control.Monad.Trans.Reader (ReaderT, runReaderT, )
import Control.Monad.Trans.RWS    (RWS, runRWS, tell, )
import Control.Monad.Trans.Class (lift, )
import qualified Control.Monad.Trans.RWS as RWS
import qualified Control.Monad.Exception.Asynchronous as Async
import Data.Char (chr, )
import qualified Data.List    as List
import qualified Data.List.HT as ListHT
import Data.Maybe.HT (toMaybe, )
import Data.Tuple.HT (forcePair, mapFst, )
import qualified Data.ByteString      as BS
import qualified Data.ByteString.Lazy as BL
import Prelude hiding (splitAt, )
type T body = RWS Stream.ConnError [body] body
class Body.C body => Body body where
   splitAt    :: Int -> body -> (body, body)
   breakAfter :: (Char  -> Bool) -> body -> (body, body)
instance Body.CharType char => Body [char] where
   splitAt      = List.splitAt
   breakAfter p = ListHT.breakAfter (p . Body.toChar)
instance Body BS.ByteString where
   splitAt = BS.splitAt
   breakAfter p s =
      forcePair $
      maybe (s,BS.empty)
         (\i -> splitAt (i+1) s)
         (BS.findIndex (p . chr . fromIntegral) s)
instance Body BL.ByteString where
   splitAt = BL.splitAt . fromIntegral
   breakAfter p s =
      let (prefix,suffix) =
             BL.break (p . chr . fromIntegral) s
      in  forcePair $
          maybe
             (prefix,suffix)
             (mapFst (BL.snoc prefix))
             (BL.uncons suffix)
withBuffer :: (Body.C body) =>
   (body -> (a, body)) ->
   Transfer.AsyncExceptional (T body) a
withBuffer f =
   Async.ExceptionalT $
   
   do buf <- RWS.get
      let (block,rest) = f buf
      RWS.put rest
      closeReason <- RWS.ask
      return $
         Async.Exceptional (toMaybe (Body.isEmpty rest) closeReason) block
transfer :: (Body body) =>
   Transfer.T (T body) body
transfer =
   Transfer.Cons {
      Transfer.readBlock  = \n -> withBuffer $ splitAt n,
      Transfer.readLine   = withBuffer $ breakAfter ('\n'==),
      Transfer.writeBlock = \str -> lift $ tell [str]
   }
run :: (Body body) =>
   Reader.T body (T body) a -> Stream.ConnError -> body -> (a, body, [body])
run m = runRWS (runReaderT m $ transfer)