module Network.HTTP.HandleStream 
       ( simpleHTTP      
       , simpleHTTP_     
       , sendHTTP        
       , sendHTTP_notify 
       , receiveHTTP     
       , respondHTTP     
       
       , simpleHTTP_debug 
       ) where
import Network.BufferType
import Network.Stream ( fmapE, Result )
import Network.StreamDebugger ( debugByteStream )
import Network.TCP (HStream(..), HandleStream )
import Network.HTTP.Base
import Network.HTTP.Headers
import Network.HTTP.Utils ( trim, readsOne )
import Data.Char (toLower)
import Data.Maybe (fromMaybe)
import Control.Exception (onException)
import Control.Monad (when)
simpleHTTP :: HStream ty => Request ty -> IO (Result (Response ty))
simpleHTTP r = do 
  auth <- getAuth r
  failHTTPS (rqURI r)
  c <- openStream (host auth) (fromMaybe 80 (port auth))
  simpleHTTP_ c r
simpleHTTP_debug :: HStream ty => FilePath -> Request ty -> IO (Result (Response ty))
simpleHTTP_debug httpLogFile r = do 
  auth <- getAuth r
  failHTTPS (rqURI r)
  c0   <- openStream (host auth) (fromMaybe 80 (port auth))
  c    <- debugByteStream httpLogFile c0
  simpleHTTP_ c r
simpleHTTP_ :: HStream ty => HandleStream ty -> Request ty -> IO (Result (Response ty))
simpleHTTP_ s r = sendHTTP s r
sendHTTP :: HStream ty => HandleStream ty -> Request ty -> IO (Result (Response ty))
sendHTTP conn rq = sendHTTP_notify conn rq (return ())
sendHTTP_notify :: HStream ty
                => HandleStream ty
                -> Request ty
                -> IO ()
                -> IO (Result (Response ty))
sendHTTP_notify conn rq onSendComplete = do
  when providedClose $ (closeOnEnd conn True)
  onException (sendMain conn rq onSendComplete)
              (close conn)
 where
  providedClose = findConnClose (rqHeaders rq)
sendMain :: HStream ty
         => HandleStream ty
         -> Request ty
         -> (IO ())
         -> IO (Result (Response ty))
sendMain conn rqst onSendComplete = do
      
      
      
  
  _ <- writeBlock conn (buf_fromStr bufferOps $ show rqst)
    
  
  _ <- writeBlock conn (rqBody rqst)
  onSendComplete
  rsp <- getResponseHead conn
  switchResponse conn True False rsp rqst
   
   
   
switchResponse :: HStream ty
               => HandleStream ty
               -> Bool 
               -> Bool 
               -> Result ResponseData
               -> Request ty
               -> IO (Result (Response ty))
switchResponse _ _ _ (Left e) _ = return (Left e)
                
                
                
switchResponse conn allow_retry bdy_sent (Right (cd,rn,hdrs)) rqst = 
   case matchResponse (rqMethod rqst) cd of
     Continue
      | not bdy_sent -> do 
        writeBlock conn (rqBody rqst) >>= either (return . Left)
           (\ _ -> do
              rsp <- getResponseHead conn
              switchResponse conn allow_retry True rsp rqst)
      | otherwise    -> do 
        rsp <- getResponseHead conn
        switchResponse conn allow_retry bdy_sent rsp rqst
     Retry -> do 
        
        _ <- writeBlock conn ((buf_append bufferOps)
                                     (buf_fromStr bufferOps (show rqst))
                                     (rqBody rqst))
        rsp <- getResponseHead conn
        switchResponse conn False bdy_sent rsp rqst
                     
     Done -> do
       when (findConnClose hdrs)
            (closeOnEnd conn True)
       return (Right $ Response cd rn hdrs (buf_empty bufferOps))
     DieHorribly str -> do
       close conn
       return (responseParseError "Invalid response:" str)
     ExpectEntity -> do
       r <- fmapE (\ (ftrs,bdy) -> Right (Response cd rn (hdrs++ftrs) bdy)) $
             maybe (maybe (hopefulTransfer bo (readLine conn) [])
                       (\ x ->
                          readsOne (linearTransfer (readBlock conn))
                                   (return$responseParseError "unrecognized content-length value" x)
                                   x)
                        cl)
                   (ifChunked (chunkedTransfer bo (readLine conn) (readBlock conn))
                              (uglyDeathTransfer "sendHTTP"))
                   tc
       case r of
         Left{} -> do
           close conn
           return r
         Right (Response _ _ hs _) -> do
           when (findConnClose hs)
                (closeOnEnd conn True)
           return r
      where
       tc = lookupHeader HdrTransferEncoding hdrs
       cl = lookupHeader HdrContentLength hdrs
       bo = bufferOps
                    
getResponseHead :: HStream ty => HandleStream ty -> IO (Result ResponseData)
getResponseHead conn = 
   fmapE (\es -> parseResponseHead (map (buf_toStr bufferOps) es))
         (readTillEmpty1 bufferOps (readLine conn))
receiveHTTP :: HStream bufTy => HandleStream bufTy -> IO (Result (Request bufTy))
receiveHTTP conn = getRequestHead >>= either (return . Left) processRequest
  where
    
   getRequestHead :: IO (Result RequestData)
   getRequestHead = do
      fmapE (\es -> parseRequestHead (map (buf_toStr bufferOps) es))
            (readTillEmpty1 bufferOps (readLine conn))
   processRequest (rm,uri,hdrs) =
      fmapE (\ (ftrs,bdy) -> Right (Request uri rm (hdrs++ftrs) bdy)) $
             maybe
              (maybe (return (Right ([], buf_empty bo))) 
                     (\ x -> readsOne (linearTransfer (readBlock conn))
                                      (return$responseParseError "unrecognized Content-Length value" x)
                                      x)
                     cl)
              (ifChunked (chunkedTransfer bo (readLine conn) (readBlock conn))
                         (uglyDeathTransfer "receiveHTTP"))
              tc
    where
     
     tc = lookupHeader HdrTransferEncoding hdrs
     cl = lookupHeader HdrContentLength hdrs
     bo = bufferOps
respondHTTP :: HStream ty => HandleStream ty -> Response ty -> IO ()
respondHTTP conn rsp = do 
  
  _ <- writeBlock conn (buf_fromStr bufferOps $ show rsp)
   
  
  _ <- writeBlock conn (rspBody rsp)
  return ()
headerName :: String -> String
headerName x = map toLower (trim x)
ifChunked :: a -> a -> String -> a
ifChunked a b s = 
  case headerName s of
    "chunked" -> a
    _ -> b