{-# LANGUAGE CPP, ForeignFunctionInterface #-}

module Darcs.Util.Download.Curl where

#ifdef HAVE_CURL

import Prelude ()
import Darcs.Prelude

import Control.Exception ( bracket )
import Control.Monad ( when )
import Foreign.C.Types ( CLong(..), CInt(..) )

import Darcs.Util.Progress ( debugMessage )

import Darcs.Util.Download.Request

import Foreign.C.String ( withCString, peekCString, CString )
import Foreign.Ptr
import Foreign.Marshal.Alloc
import Foreign.Storable

setDebugHTTP :: IO ()
setDebugHTTP = curl_enable_debug

requestUrl :: String -> FilePath -> Cachable -> IO String
requestUrl u f cache =
  withCString u $ \ustr ->
  withCString f $ \fstr ->
  bracket malloc free $ \ errorPointer -> do
    e <- curl_request_url ustr fstr (cachableToInt cache) errorPointer >>= peekCString
    errorNum <- peek errorPointer
    when (errorNum == 90 ) $ debugMessage "The environment variable DARCS_CONNECTION_TIMEOUT is not a number"
    return e

waitNextUrl :: IO (String, String, Maybe ConnectionError)
waitNextUrl =
  bracket malloc free $ \ errorPointer ->
  bracket malloc free $ \ httpErrorPointer -> do
    e <- curl_wait_next_url errorPointer httpErrorPointer >>= peekCString
    ce <- do
           errorNum <- peek errorPointer
           if null e then return Nothing
             else return $
              case errorNum of
                6  -> Just CouldNotResolveHost
                7  -> Just CouldNotConnectToServer
                28 -> Just OperationTimeout
                _  -> Nothing
    u <- curl_last_url >>= peekCString
    httpErrorCode <- peek httpErrorPointer
    let detailedErrorMessage = if httpErrorCode > 0
                               then e ++ " " ++ show httpErrorCode
                               else e
    return (u, detailedErrorMessage, ce)

pipeliningEnabled :: IO Bool
pipeliningEnabled = do
  r <- curl_pipelining_enabled
  return $ r /= 0

cachableToInt :: Cachable -> CInt
cachableToInt Cachable = -1
cachableToInt Uncachable = 0
cachableToInt (MaxAge n) = n

foreign import ccall "hscurl.h curl_request_url"
  curl_request_url :: CString -> CString -> CInt -> Ptr CInt -> IO CString

foreign import ccall "hscurl.h curl_wait_next_url"
  curl_wait_next_url :: Ptr CInt -> Ptr CLong-> IO CString

foreign import ccall "hscurl.h curl_last_url"
  curl_last_url :: IO CString

foreign import ccall "hscurl.h curl_enable_debug"
  curl_enable_debug :: IO ()

foreign import ccall "hscurl.h curl_pipelining_enabled"
  curl_pipelining_enabled :: IO CInt

#endif
