module Wobsurv.Util.HTTP.Renderer where

import BasePrelude
import Control.Monad.Trans.Writer
import Data.ByteString.Builder
import Wobsurv.Util.HTTP.Model
import qualified Pipes
import qualified Pipes.ByteString
import qualified Data.ByteString
import qualified Data.ByteString.Lazy


toProducer :: Monad m => Builder -> Pipes.Producer BS m ()
toProducer =
  Pipes.ByteString.fromLazy . toLazyByteString

toByteString :: Builder -> Data.ByteString.ByteString
toByteString =
  Data.ByteString.Lazy.toStrict . toLazyByteString

newLine :: Builder
newLine =
  string7 "\r\n"

statusLine :: Version -> Status -> Builder
statusLine versionV statusV =
  version versionV <> char7 ' ' <> status statusV <> newLine

relativeURI :: RelativeURI -> Builder
relativeURI (path, query, fragment) =
  execWriter $ do
    tell $ char7 '/'
    traverse_ (tell . byteString) path
    traverse_ (tell . (char7 '?' <>) . byteString) query
    traverse_ (tell . (char7 '#' <>) . byteString) fragment

version :: Version -> Builder =
  \(major, minor) ->
    string7 "HTTP/" <> wordDec major <> char7 '.' <> wordDec minor

status :: Status -> Builder
status (code, message) =
  wordDec code <> char7 ' ' <> byteString message

headers :: [Header] -> Builder
headers =
  mconcat . map header

header :: Header -> Builder
header =
  \case
    ConnectionHeader x -> connectionHeader x
    ContentLengthHeader x -> contentLengthHeader x
    ContentTypeHeader x -> contentTypeHeader x
    KeepAliveHeader x -> keepAliveHeader x

connectionHeader :: ConnectionHeader -> Builder
connectionHeader keepAlive =
  string7 "Connection: " <> 
  string7 (if keepAlive then "keep-alive" else "close") <>
  newLine

contentLengthHeader :: ContentLengthHeader -> Builder
contentLengthHeader length =
  string7 "Content-Length: " <> wordDec length <> newLine

contentTypeHeader :: ContentTypeHeader -> Builder
contentTypeHeader (mimeType, charsetV) =
  string7 "Content-Type: " <> fields <> newLine
  where
    fields =
      mconcat $ intersperse ";" $ catMaybes $
        [ Just (byteString mimeType), 
          charsetField <$> charsetV ] 
      where
        charsetField x =
          string7 "charset=" <> charset x

keepAliveHeader :: KeepAliveHeader -> Builder
keepAliveHeader (timeout, max) =
  string7 "Keep-Alive: " <> fields <> newLine
  where
    fields =
      mconcat $ intersperse ", " $ catMaybes $
        [ Just $ timeoutField $ timeout, 
          maxField <$> max ]
      where
        timeoutField x =
          string7 "timeout=" <> wordDec x
        maxField x =
          string7 "max=" <> wordDec x

charset :: Charset -> Builder
charset =
  \case
    UTF8 -> "utf-8"

method :: Method -> Builder
method =
  either standardMethod byteString

standardMethod :: StandardMethod -> Builder
standardMethod = 
  \case
    Options -> string7 "OPTIONS"
    Get     -> string7 "GET"
    Head    -> string7 "HEAD"
    Post    -> string7 "POST"
    Put     -> string7 "PUT"
    Delete  -> string7 "DELETE"
    Trace   -> string7 "TRACE"
    Connect -> string7 "CONNECT"