{- |
Module representing a JSON-API meta object.

Specification: <http://jsonapi.org/format/#document-meta>
-}
module Network.JSONApi.Meta
( Meta
, MetaObject (..)
, Pagination (..)
, mkMeta
)where

import           Data.Aeson (ToJSON, FromJSON, Object, toJSON)
import           Data.Aeson.TH
import           Data.HashMap.Strict as HM
import           Data.Text (Text)

import qualified GHC.Generics as G

{- |
Type representing a JSON-API meta object.

Meta is an abstraction around an underlying Map consisting of
resource-specific metadata.

Example JSON:
@
  "meta": {
    "copyright": "Copyright 2015 Example Corp.",
    "authors": [
      "Andre Dawson",
      "Kirby Puckett",
      "Don Mattingly",
      "Ozzie Guillen"
    ]
  }
@

Specification: <http://jsonapi.org/format/#document-meta>
-}
newtype Meta = Meta Object
  deriving (Show, Eq, G.Generic)

instance ToJSON Meta
instance FromJSON Meta

{- |
Convienience class for constructing a Meta type

Example usage:
@
  data Pagination = Pagination
    { currentPage :: Int
    , totalPages :: Int
    } deriving (Show, Generic)

  instance ToJSON Pagination
  instance MetaObject Pagination where
    typeName _ = "pagination"
@
-}
class (ToJSON a) => MetaObject a where
  typeName :: a -> Text

{- |
Convienience constructor function for the Meta type

Useful on its own or in combination with Meta's monoid instance

Example usage:
See MetaSpec.hs for an example
-}
mkMeta :: (MetaObject a) => a -> Meta
mkMeta obj = Meta $ HM.singleton (typeName obj) (toJSON obj)

{- |
Pagination is arguably a meta object not covered by the Spec. The spec instead opts for
links which are supported by this library. However if you would like to throw a generic
Pagination meta object into your response payload this type may be used.
-}
data Pagination = Pagination {
  pageSize :: Maybe Int
  , currentPage :: Maybe Int
  , totalDocuments :: Maybe Int
} deriving (Eq, Show)

$(deriveJSON defaultOptions ''Pagination)

instance MetaObject Pagination where
  typeName _ = "pagination"