{-#LANGUAGE OverloadedStrings #-}
module Servant.JS.Vanilla where

import Prelude ()
import Prelude.Compat

import           Control.Lens
import           Data.Maybe (isJust)
import           Data.Text (Text)
import           Data.Text.Encoding (decodeUtf8)
import qualified Data.Text as T
import           Servant.Foreign hiding (header)
import           Servant.JS.Internal

-- | Generate vanilla javascript functions to make AJAX requests
--   to your API, using /XMLHttpRequest/. Uses 'defCommonGeneratorOptions'
--   for the 'CommonGeneratorOptions'.
vanillaJS :: JavaScriptGenerator
vanillaJS = mconcat . map generateVanillaJS

-- | Generate vanilla javascript functions to make AJAX requests
--   to your API, using /XMLHttpRequest/. Lets you specify your
--   own options.
vanillaJSWith :: CommonGeneratorOptions -> JavaScriptGenerator
vanillaJSWith opts = mconcat . map (generateVanillaJSWith opts)

-- | js codegen using XmlHttpRequest using default generation options
generateVanillaJS :: AjaxReq -> Text
generateVanillaJS = generateVanillaJSWith defCommonGeneratorOptions

-- | js codegen using XmlHttpRequest
generateVanillaJSWith :: CommonGeneratorOptions -> AjaxReq -> Text
generateVanillaJSWith opts req = "\n" <>
    fname <> " = function(" <> argsStr <> ") {\n"
 <> "  var xhr = new XMLHttpRequest();\n"
 <> "  xhr.open('" <> decodeUtf8 method <> "', " <> url <> ", true);\n"
 <>    reqheaders
 <> "  xhr.setRequestHeader('Accept', 'application/json');\n"
 <> (if isJust (req ^. reqBody) && (req ^. reqBodyContentType == ReqBodyJSON)  then "  xhr.setRequestHeader('Content-Type', 'application/json');\n" else "")
 <> "  xhr.onreadystatechange = function () {\n"
 <> "    var res = null;\n"
 <> "    if (xhr.readyState === 4) {\n"
 <> "      if (xhr.status === 204 || xhr.status === 205) {\n"
 <> "        " <> onSuccess <> "();\n"
 <> "      } else if (xhr.status >= 200 && xhr.status < 300) {\n"
 <> "        try { res = JSON.parse(xhr.responseText); } catch (e) { " <> onError <> "(e); }\n"
 <> "        if (res) " <> onSuccess <> "(res);\n"
 <> "      } else {\n"
 <> "        try { res = JSON.parse(xhr.responseText); } catch (e) { " <> onError <> "(e); }\n"
 <> "        if (res) " <> onError <> "(res);\n"
 <> "      }\n"
 <> "    }\n"
 <> "  };\n"
 <> "  xhr.send(" <> dataBody <> ");\n"
 <> "};\n"

  where argsStr = T.intercalate ", " args
        args = captures
            ++ map (view $ queryArgName . argPath) queryparams
            ++ body
            ++ map ( toValidFunctionName
                   . (<>) "header"
                   . view (headerArg . argPath)
                   ) hs
            ++ [onSuccess, onError]

        captures = map (view argPath . captureArg)
                 . filter isCapture
                 $ req ^. reqUrl.path

        hs = req ^. reqHeaders

        queryparams = req ^.. reqUrl.queryStr.traverse

        body = if isJust(req ^. reqBody)
                 then [requestBody opts]
                 else []

        onSuccess = successCallback opts
        onError = errorCallback opts

        dataBody =
          if isJust (req ^. reqBody)
            then if (req ^. reqBodyContentType == ReqBodyJSON) then "JSON.stringify(body)" else "body"
            else "null"


        reqheaders =
          if null hs
            then ""
            else headersStr <> "\n"

          where
            headersStr = T.intercalate "\n" $ map headerStr hs
            headerStr header = "  xhr.setRequestHeader(\"" <>
              header ^. headerArg . argPath <>
              "\", " <> toJSHeader header <> ");"

        namespace = if moduleName opts == ""
                       then "var "
                       else (moduleName opts) <> "."
        fname = namespace <> (toValidFunctionName (functionNameBuilder opts $ req ^. reqFuncName))

        method = req ^. reqMethod
        url = if url' == "'" then "'/'" else url'
        url' = "'"
           <> urlPrefix opts
           <> urlArgs
           <> queryArgs

        urlArgs = jsSegments
                $ req ^.. reqUrl.path.traverse

        queryArgs = if null queryparams
                      then ""
                      else " + '?" <> jsParams queryparams