module System.IO.Uniform (
  UniformIO(..),
  TlsSettings(..),
  SomeIO(..),
  mapOverInput,
  uGetContents
  ) where
import Data.ByteString (ByteString)
import qualified Data.ByteString.Lazy as LBS
import Control.Exception
import System.IO.Error
import Data.Default.Class
class UniformIO a where
  
  uRead  :: a -> Int -> IO ByteString
  
  
  
  uPut   :: a -> ByteString -> IO ()
  
  
  
  
  uClose :: a -> IO ()
  
  
  
  startTls :: TlsSettings -> a -> IO a
  
  
  
  isSecure :: a -> Bool
  
data SomeIO = forall a. (UniformIO a) => SomeIO a
instance UniformIO SomeIO where
  uRead (SomeIO s) = uRead s
  uPut (SomeIO s)  = uPut s
  uClose (SomeIO s) = uClose s
  startTls set (SomeIO s) = SomeIO <$> startTls set s
  isSecure (SomeIO s) = isSecure s
data TlsSettings = TlsSettings {tlsPrivateKeyFile :: String, tlsCertificateChainFile :: String, tlsDHParametersFile :: String} deriving (Read, Show)
instance Default TlsSettings where
  def = TlsSettings "" "" ""
  
mapOverInput :: forall a io. UniformIO io => io -> Int -> (a -> ByteString -> IO a) -> a -> IO a
mapOverInput io block f initial = do
  a <- tryIOError $ uRead io block
  case a of
    Left e -> if isEOFError e then return initial else throw e 
    Right dt -> do
      i <- f initial dt
      mapOverInput io block f i
uGetContents :: UniformIO io => io -> Int -> IO LBS.ByteString
uGetContents io block = LBS.fromChunks <$> mapOverInput io block atEnd []
  where
    atEnd :: [ByteString] -> ByteString -> IO [ByteString]
    atEnd bb b = return $ bb ++ [b]