{-# LANGUAGE OverloadedStrings #-}
-- | Low level XMLHttpRequest support. IE6 and older are not supported.
module Haste.Ajax (Method (..), URL, ajaxRequest, noParams) where
import Haste.Foreign
import Haste.Prim
import Haste.Prim.JSType
import Control.Monad.IO.Class
import Control.Monad (join)

ajaxReq :: Method   -- method (GET/POST)
        -> JSString -- URL
        -> Bool     -- async?
        -> JSString -- POST data
        -> (Maybe JSString -> IO ()) -- callback
        -> IO ()
ajaxReq = ffi "(function(method, url, async, postdata, cb) {\
    \var xhr = new XMLHttpRequest();\
    \xhr.open(method, url, async);\
    \if(method == 'POST') {\
        \xhr.setRequestHeader('Content-type',\
                             \'application/x-www-form-urlencoded');\
    \}\
    \xhr.onreadystatechange = function() {\
        \if(xhr.readyState == 4) {\
            \cb(xhr.status == 200 ? xhr.responseText : null);\
        \}\
    \};\
    \xhr.send(postdata);})"

data Method = GET | POST deriving Show

instance ToAny Method where
  toAny GET  = toAny ("GET" :: JSString)
  toAny POST = toAny ("POST" :: JSString)

-- | Pass to 'ajaxRequest' instead of @[]@ when no parameters are needed, to
--   avoid type ambiguity errors.
noParams :: [((), ())]
noParams = []

-- | Perform an AJAX request.
ajaxRequest :: (MonadIO m, JSType a, JSType b, JSType c)
            => Method   -- ^ GET or POST. For GET, pass all params in URL.
                        --   For POST, pass all params as post data.
            -> URL      -- ^ URL to make AJAX request to.
            -> [(a, b)] -- ^ A list of (key, value) parameters.
            -> (Maybe c -> IO ()) -- ^ Callback to invoke on completion.
            -> m ()
ajaxRequest m url kv cb = liftIO $ do
    _ <- ajaxReq m url' True pd (cb . join . fmap fromJSString)
    return ()
  where
    url' = case m of
           GET
             | null kv   -> toJSString url
             | otherwise -> catJSStr "?" [toJSString url, toQueryString kv]
           POST -> toJSString url
    pd = case m of
           GET -> ""
           POST
             | null kv   -> ""
             | otherwise -> toQueryString kv

toQueryString :: (JSType a, JSType b) =>[(a, b)] -> JSString
toQueryString = catJSStr "&" . map f
  where f (k, v) = catJSStr "=" [toJSString k,toJSString v]