{-# LANGUAGE DeriveFunctor              #-}
{-# LANGUAGE OverloadedStrings          #-}
{-# LANGUAGE RecordWildCards            #-}

module Data.API.Doc.Types
    ( URL
    , HTTPMethod
    , StatusCode
    , Call(..)
    , Header(..)
    , Param(..)
    , View(..)
    , Sample(..)
    , Body(..)
    , DocInfo(..)
    , renderAPIType
    , renderBodyType
    , mk_link
    ) where

import           Data.API.PP
import           Data.API.Types

import qualified Data.Text                      as T
import           Text.Printf

type URL        = String
type HTTPMethod = String
type StatusCode = Int

-- | Documents a single method call on a resource in a web application
data Call
    = Call
        { Call -> HTTPMethod
call_http_method   :: HTTPMethod               -- ^ HTTP method being documented
        , Call -> [HTTPMethod]
call_path          :: [String]                 -- ^ Relative URL path of the resource
        , Call -> HTTPMethod
call_description   :: String                   -- ^ Free-form text description
        , Call -> Bool
call_auth_required :: Bool                     -- ^ Does the call require authentication?
        , Call -> [Header]
call_headers       :: [Header]                 -- ^ HTTP headers relevant to the call
        , Call -> Maybe (APIType, HTTPMethod)
call_body          :: Maybe (APIType, String)  -- ^ Type and example of request body
        , Call -> [Param]
call_params        :: [Param]                  -- ^ Query parameters relevant to the call
        , Call -> [View]
call_views         :: [View]                   -- ^ Available views of the result data
        , Call -> [Sample]
call_samples       :: [Sample]                 -- ^ Example responses
        }
    deriving (Int -> Call -> ShowS
[Call] -> ShowS
Call -> HTTPMethod
(Int -> Call -> ShowS)
-> (Call -> HTTPMethod) -> ([Call] -> ShowS) -> Show Call
forall a.
(Int -> a -> ShowS)
-> (a -> HTTPMethod) -> ([a] -> ShowS) -> Show a
showList :: [Call] -> ShowS
$cshowList :: [Call] -> ShowS
show :: Call -> HTTPMethod
$cshow :: Call -> HTTPMethod
showsPrec :: Int -> Call -> ShowS
$cshowsPrec :: Int -> Call -> ShowS
Show)

-- | Documents a HTTP header that may be supplied to a 'Call'
data Header
    = Header
        { Header -> HTTPMethod
header_name     :: String  -- ^ Header name
        , Header -> HTTPMethod
header_expl     :: String  -- ^ Example value for header
        , Header -> HTTPMethod
header_desc     :: String  -- ^ Free-form text description
        , Header -> APIType
header_type     :: APIType -- ^ Type of data in header
        , Header -> Bool
header_required :: Bool    -- ^ Is including the header with the request mandatory?
        } deriving (Int -> Header -> ShowS
[Header] -> ShowS
Header -> HTTPMethod
(Int -> Header -> ShowS)
-> (Header -> HTTPMethod) -> ([Header] -> ShowS) -> Show Header
forall a.
(Int -> a -> ShowS)
-> (a -> HTTPMethod) -> ([a] -> ShowS) -> Show a
showList :: [Header] -> ShowS
$cshowList :: [Header] -> ShowS
show :: Header -> HTTPMethod
$cshow :: Header -> HTTPMethod
showsPrec :: Int -> Header -> ShowS
$cshowsPrec :: Int -> Header -> ShowS
Show)

-- | Documents a URL query parameter that may be included with a 'Call'
data Param
    = Param
        { Param -> HTTPMethod
param_name     :: String                -- ^ Parameter name
        , Param -> HTTPMethod
param_expl     :: String                -- ^ Example value for parameter
        , Param -> HTTPMethod
param_desc     :: String                -- ^ Free-form text description
        , Param -> Either HTTPMethod APIType
param_type     :: Either String APIType -- ^ Type of data in the parameter
        , Param -> Bool
param_required :: Bool                  -- ^ Is including the parameter mandatory?
        } deriving (Int -> Param -> ShowS
[Param] -> ShowS
Param -> HTTPMethod
(Int -> Param -> ShowS)
-> (Param -> HTTPMethod) -> ([Param] -> ShowS) -> Show Param
forall a.
(Int -> a -> ShowS)
-> (a -> HTTPMethod) -> ([a] -> ShowS) -> Show a
showList :: [Param] -> ShowS
$cshowList :: [Param] -> ShowS
show :: Param -> HTTPMethod
$cshow :: Param -> HTTPMethod
showsPrec :: Int -> Param -> ShowS
$cshowsPrec :: Int -> Param -> ShowS
Show)

-- | Documents a specific view of the result data available in a 'Call'
data View
    = View
        { View -> HTTPMethod
view_id     :: String  -- ^ View name
        , View -> APIType
view_type   :: APIType -- ^ Type of result data returned
        , View -> HTTPMethod
view_doc    :: String  -- ^ Free-form text description
        , View -> [Param]
view_params :: [Param] -- ^ Query parameters that may be supplied for this view
        } deriving (Int -> View -> ShowS
[View] -> ShowS
View -> HTTPMethod
(Int -> View -> ShowS)
-> (View -> HTTPMethod) -> ([View] -> ShowS) -> Show View
forall a.
(Int -> a -> ShowS)
-> (a -> HTTPMethod) -> ([a] -> ShowS) -> Show a
showList :: [View] -> ShowS
$cshowList :: [View] -> ShowS
show :: View -> HTTPMethod
$cshow :: View -> HTTPMethod
showsPrec :: Int -> View -> ShowS
$cshowsPrec :: Int -> View -> ShowS
Show)

-- | Example response data from a 'Call'
data Sample
    = Sample
        { Sample -> Int
sample_status   :: StatusCode    -- ^ HTTP status code for this example response
        , Sample -> Body APIType
sample_type     :: Body APIType  -- ^ Type of example response
        , Sample -> Maybe HTTPMethod
sample_response :: Maybe String  -- ^ Content of response, or 'Nothing' for empty response
        } deriving (Int -> Sample -> ShowS
[Sample] -> ShowS
Sample -> HTTPMethod
(Int -> Sample -> ShowS)
-> (Sample -> HTTPMethod) -> ([Sample] -> ShowS) -> Show Sample
forall a.
(Int -> a -> ShowS)
-> (a -> HTTPMethod) -> ([a] -> ShowS) -> Show a
showList :: [Sample] -> ShowS
$cshowList :: [Sample] -> ShowS
show :: Sample -> HTTPMethod
$cshow :: Sample -> HTTPMethod
showsPrec :: Int -> Sample -> ShowS
$cshowsPrec :: Int -> Sample -> ShowS
Show)

-- | Type for 'Sample' response body, parameterised by possible JSON types
data Body t = EmptyBody        -- ^ An empty response
            | JSONBody  t      -- ^ A JSON response of the given type
            | OtherBody String -- ^ A non-empty, non-JSON response
  deriving (a -> Body b -> Body a
(a -> b) -> Body a -> Body b
(forall a b. (a -> b) -> Body a -> Body b)
-> (forall a b. a -> Body b -> Body a) -> Functor Body
forall a b. a -> Body b -> Body a
forall a b. (a -> b) -> Body a -> Body b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: a -> Body b -> Body a
$c<$ :: forall a b. a -> Body b -> Body a
fmap :: (a -> b) -> Body a -> Body b
$cfmap :: forall a b. (a -> b) -> Body a -> Body b
Functor, Int -> Body t -> ShowS
[Body t] -> ShowS
Body t -> HTTPMethod
(Int -> Body t -> ShowS)
-> (Body t -> HTTPMethod) -> ([Body t] -> ShowS) -> Show (Body t)
forall t. Show t => Int -> Body t -> ShowS
forall t. Show t => [Body t] -> ShowS
forall t. Show t => Body t -> HTTPMethod
forall a.
(Int -> a -> ShowS)
-> (a -> HTTPMethod) -> ([a] -> ShowS) -> Show a
showList :: [Body t] -> ShowS
$cshowList :: forall t. Show t => [Body t] -> ShowS
show :: Body t -> HTTPMethod
$cshow :: forall t. Show t => Body t -> HTTPMethod
showsPrec :: Int -> Body t -> ShowS
$cshowsPrec :: forall t. Show t => Int -> Body t -> ShowS
Show)

-- | Record of arguments that must be supplied to generate HTML
-- documentation for a 'Call'
data DocInfo
    = DocInfo
        { DocInfo -> HTTPMethod -> [HTTPMethod] -> HTTPMethod
doc_info_call_url :: HTTPMethod -> [String] -> URL
          -- ^ URL for individual call documentation from the index
        , DocInfo -> TypeName -> HTTPMethod
doc_info_type_url :: TypeName -> URL
          -- ^ URL for documentation of an API type
        }

renderBodyType :: DocInfo -> Body APIType -> String
renderBodyType :: DocInfo -> Body APIType -> HTTPMethod
renderBodyType DocInfo
_  Body APIType
EmptyBody     = HTTPMethod
"empty"
renderBodyType DocInfo
di (JSONBody APIType
ty) = HTTPMethod
"json&nbsp;&nbsp;" HTTPMethod -> ShowS
forall a. [a] -> [a] -> [a]
++ DocInfo -> APIType -> HTTPMethod
renderAPIType DocInfo
di APIType
ty
renderBodyType DocInfo
_  (OtherBody HTTPMethod
s) = HTTPMethod
s

renderAPIType :: DocInfo -> APIType -> String
renderAPIType :: DocInfo -> APIType -> HTTPMethod
renderAPIType DocInfo
di (TyList  APIType
ty  ) = HTTPMethod
"[" HTTPMethod -> ShowS
forall a. [a] -> [a] -> [a]
++ DocInfo -> APIType -> HTTPMethod
renderAPIType DocInfo
di APIType
ty HTTPMethod -> ShowS
forall a. [a] -> [a] -> [a]
++ HTTPMethod
"]"
renderAPIType DocInfo
di (TyMaybe APIType
ty  ) = HTTPMethod
"?" HTTPMethod -> ShowS
forall a. [a] -> [a] -> [a]
++ DocInfo -> APIType -> HTTPMethod
renderAPIType DocInfo
di APIType
ty
renderAPIType DocInfo
di (TyName  TypeName
tn  ) = HTTPMethod -> ShowS
mk_link (DocInfo -> TypeName -> HTTPMethod
doc_info_type_url DocInfo
di TypeName
tn) (Text -> HTTPMethod
T.unpack (TypeName -> Text
_TypeName TypeName
tn))
renderAPIType DocInfo
_  (TyBasic BasicType
bt  ) = BasicType -> HTTPMethod
forall t. PP t => t -> HTTPMethod
pp BasicType
bt
renderAPIType DocInfo
_  APIType
TyJSON         = HTTPMethod
"json"

mk_link :: URL -> String -> String
mk_link :: HTTPMethod -> ShowS
mk_link = HTTPMethod -> HTTPMethod -> ShowS
forall r. PrintfType r => HTTPMethod -> r
printf HTTPMethod
"<b><a class='reflink' href='%s' >%s</a></b>"