module Network.Wai.Frontend.MonadCGI
    ( cgiToApp
    , cgiToAppGeneric
    ) where

import Network.Wai
import Network.CGI.Monad
import Network.CGI.Protocol
import Network.HTTP.Types (Status (..))
import Control.Monad.IO.Class (liftIO)
import Data.CaseInsensitive (original)

import Data.Conduit.Lazy (lazyConsume)

import qualified Data.Map as Map
import qualified Data.ByteString.Lazy as BS
import qualified Data.ByteString.Char8 as S8

import Control.Arrow (first)
import Data.Char (toUpper)
import Data.String (fromString)

safeRead :: Read a => a -> String -> a
safeRead d s = case reads s of
                ((x, _):_) -> x
                _ -> d

cgiToApp :: CGI CGIResult -> Application
cgiToApp = cgiToAppGeneric id

cgiToAppGeneric :: Monad m
                => (m (Headers, CGIResult) -> IO (Headers, CGIResult))
                -> CGIT m CGIResult
                -> Application
cgiToAppGeneric toIO cgi env = do
    input <- fmap BS.fromChunks $ lazyConsume $ requestBody env
    let vars = map (first fixVarName . go) (requestHeaders env)
               ++ getCgiVars env
        (inputs, body') = decodeInput vars input
        req = CGIRequest
                { cgiVars = Map.fromList $ vars
                , cgiInputs = inputs
                , cgiRequestBody = body'
                }
    (headers'', output') <- liftIO $ toIO $ runCGIT cgi req
    let output = case output' of
                    CGIOutput bs -> bs
                    CGINothing -> BS.empty
    let headers' = map (\(HeaderName x, y) ->
                         (fromString x, S8.pack y)) headers''
    let status' = case lookup (fromString "Status") headers' of
                    Nothing -> 200
                    Just s -> safeRead 200 $ S8.unpack s
    return $ responseLBS (Status status' S8.empty) headers' output
  where
    go (x, y) = (S8.unpack $ original x, S8.unpack y)

fixVarName :: String -> String
fixVarName = ((++) $ "HTTP_") . map fixVarNameChar

fixVarNameChar :: Char -> Char
fixVarNameChar '-' = '_'
fixVarNameChar c = toUpper c

getCgiVars :: Request -> [(String, String)]
getCgiVars e =
    [ ("PATH_INFO", S8.unpack $ rawPathInfo e)
    , ("REQUEST_METHOD", show $ requestMethod e)
    , ("QUERY_STRING", S8.unpack $ rawQueryString e)
    , ("SERVER_NAME", S8.unpack $ serverName e)
    , ("SERVER_PORT", show $ serverPort e)
    ]