{- |
Copyright: 2009, Henning Thielemann

Unified interface to String and ByteStrings.
-}
module Network.MoHWS.Stream where

import Network.MoHWS.ParserUtility (crLf, )

import qualified System.IO as IO
import Numeric (showHex, )

import qualified Network.MoHWS.ByteString as BU
import qualified Data.ByteString.Lazy.Char8 as BL
import qualified Data.ByteString.Char8 as BS

import qualified Data.List.HT as ListHT
import qualified Data.List    as List
import Data.Monoid (Monoid, )

import Prelude hiding (length, drop, )


class Monoid stream => C stream where
   fromString :: Int -> String -> stream
   toString :: stream -> String
   isEmpty :: stream -> Bool
   length :: stream -> Integer
   isPrefixOf :: stream -> stream -> Bool
   break :: (Char -> Bool) -> stream -> (stream, stream)
   drop :: Int -> stream -> stream
   read :: IO.Handle -> Integer -> IO stream
   readAll :: Int -> IO.Handle -> IO stream
   write :: IO.Handle -> stream -> IO ()
   writeChunked :: Int -> IO.Handle -> stream -> IO ()

class Eq char => CharType char where
   fromChar :: Char -> char
   toChar   :: char -> Char

instance CharType Char where
   fromChar = id
   toChar   = id

instance CharType char => C [char] where
   fromString _chunkSize = map fromChar
   toString = map toChar
   isEmpty = null
   length = fromIntegral . List.length
   isPrefixOf = List.isPrefixOf
   break p = List.break (p . toChar)
   drop = List.drop
   read h n = fmap (map fromChar) $ BU.hGetChars h (fromInteger n)
   readAll _chunkSize = fmap (map fromChar) . IO.hGetContents
   write h = IO.hPutStr h . map toChar
   writeChunked chunkSize h =
      IO.hPutStr h .
      foldr ($) "" .
      map (\chunk ->
              showHex (length chunk) . showString crLf .
              showString (map toChar chunk) . showString crLf) .
      ListHT.sliceVertical chunkSize

instance C BL.ByteString where
--   fromString = BL.pack
   fromString chunkSize =
      BL.fromChunks .
      map BS.pack .
      ListHT.sliceVertical chunkSize
   toString = BL.unpack
   isEmpty = BL.null
   length = fromIntegral . BL.length
   isPrefixOf = BL.isPrefixOf
   break = BL.break
   drop = BL.drop . fromIntegral
   read h n = BL.hGet h (fromInteger n)
   readAll = BU.hGetContentsN
   write = BL.hPut
   writeChunked _chunkSize h str =
      flip mapM_ (BL.toChunks str) $ \chunk ->
         do IO.hPutStr h $ showHex (BS.length chunk) crLf
            BS.hPut h chunk
            IO.hPutStr h $ crLf