{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE CPP #-}

module Data.Aviation.Aip.HttpRequest(
  aipRequestGet
, aipRequestPost
, aipRequestMethod
, doRequest
, doRequest'
, doGetRequest
, doPostRequest
, requestAipContents
, downloadHref
) where

import Control.Category((.))
import Control.Applicative(pure)
import Control.Lens
import Control.Monad.IO.Class(liftIO)
import Network.HTTP(HandleStream, getAuth, openStream, host, normalizeRequest, defaultNormalizeRequestOptions, close)
import qualified Data.ByteString.Lazy as LazyByteString(writeFile)
import Control.Monad.Trans.Except(ExceptT(ExceptT))
import Data.Aviation.Aip.AipCon(AipCon(AipCon))
import Data.Aviation.Aip.Log(aiplog)
import Data.Aviation.Aip.ConnErrorHttp4xx(ConnErrorHttp4xx(IsConnError, Http4xx))
import Data.Aviation.Aip.Href(Href(Href))
import Data.Bool(Bool(True), bool)
import Data.Either(Either(Left, Right))
import Data.Eq(Eq((==)))
#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
import Data.Foldable(elem)
import Data.Functor((<$>))
#endif
import Data.Function(($))
import Data.List(isPrefixOf, dropWhile)
import Data.Maybe(Maybe(Just))
import Data.Semigroup(Semigroup((<>)))
import Data.String(String)
import Network.HTTP(HStream, Request, RequestMethod(GET, POST), mkRequest, setRequestBody, simpleHTTP, simpleHTTP_, rspCode, rspBody)
import Network.BufferType(BufferType)
import Network.URI(URI(URI), URIAuth(URIAuth))
import Prelude(Show(show))
import System.Directory(createDirectoryIfMissing)
import System.FilePath(FilePath, splitFileName, isPathSeparator, (</>))

aipRequestGet ::
  BufferType ty =>
  Href
  -> String
  -> Request ty
aipRequestGet =
  aipRequestMethod GET

aipRequestPost ::
  BufferType ty =>
  Href
  -> String
  -> Request ty
aipRequestPost =
  aipRequestMethod POST

aipRequestMethod ::
  BufferType ty =>
  RequestMethod
  -> Href
  -> String
  -> Request ty
aipRequestMethod m (Href s) z =
  let s' = bool ("/aip/" <> s) s ("/aip/" `isPrefixOf` s)
  in  mkRequest m (URI "http:" (Just (URIAuth "" "www.airservicesaustralia.com" "")) s' z "")

doRequest ::
  HStream a =>
  Request a
  -> AipCon a
doRequest r =
  AipCon . pure .
  ExceptT $
    do  x <- simpleHTTP r
        pure $
          case x of
            Left e ->
              Left (IsConnError e)
            Right c ->
              let (r1, r2, r3) = rspCode c
              in  if r1 == 4 then
                    Left (Http4xx r2 r3)
                  else
                    Right (rspBody c)

doRequest' ::
  HStream a =>
  Request a
  -> HandleStream a
  -> AipCon a
doRequest' r h =
  AipCon . pure .
  ExceptT $
    do  x <- simpleHTTP_ h r
        pure $
          case x of
            Left e ->
              Left (IsConnError e)
            Right c ->
              let (r1, r2, r3) = rspCode c
              in  if r1 == 4 then
                    Left (Http4xx r2 r3)
                  else
                    Right (rspBody c)

doGetRequest ::
  HStream a =>
  Href
  -> String
  -> AipCon a
doGetRequest s z =
  doRequest (aipRequestGet s z)

doPostRequest ::
  HStream a =>
  Href
  -> String
  -> AipCon a
doPostRequest s z =
  doRequest (aipRequestPost s z)

requestAipContents ::
  AipCon String
requestAipContents =
  let r = setRequestBody
            (aipRequestPost (Href "aip.asp") "?pg=10")
            ("application/x-www-form-urlencoded", "Submit=I+Agree&check=1")
  in  doRequest r

downloadHref ::
  FilePath
  -> Href
  -> AipCon FilePath
downloadHref d hf =
  do  let q = aipRequestGet hf ""
      aiplog ("making request for aip document " <> show q)
      auth <- getAuth q
      aiplog ("making request for aip document with auth " <> show auth)
      c <- liftIO $ openStream (host auth) 80
      r <- doRequest' (normalizeRequest defaultNormalizeRequestOptions q) c
      let (j, k) = splitFileName (hf ^. _Wrapped)
      let ot = d </> dropWhile isPathSeparator j
      aiplog ("output directory for aip document " <> ot)
      do  liftIO $ createDirectoryIfMissing True ot
          let ot' = ot </> k
          let otw =
#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
                    let win = "/\\:*\"?<>|"
                        repl ch = bool ch '_' (ch `elem` win)
                    in  repl <$> ot'
#else
                    ot'
#endif                    
          aiplog ("writing aip document " <> otw)
          liftIO $ LazyByteString.writeFile otw r
          liftIO $ close c
          pure ot'