{-# language FlexibleInstances #-}
{-# language OverloadedStrings #-}
module RubiX.RubixResponse
  ( ToResponse(..)
  , Json(..)
  , respond
  , respondWith
  ) where

import Data.Function ((&))
import Control.Monad.Except
import qualified Data.Text as DT
import qualified Data.Text.Lazy as DTL
import Data.Aeson as Aeson

import Network.Wai as NW
import Network.HTTP.Types.Status

import Text.Blaze.Html (Html)
import Text.Blaze.Html.Renderer.Text (renderHtml)

import RubiX.Utils
import RubiX.Types

-- values should be JSON encoded sent with the "application/json"
newtype Json a = Json a
  deriving Show

-- This class represents all types which can be converted into a valid Response
class ToResponse c where
  toResponse :: c -> NW.Response

-- The following functions were inspried by Happstack server response
instance ToResponse Aeson.Value where
    toResponse = makeResponse ok200 "application/json" . fromLazyByteString . encode

instance ToResponse String where
  toResponse = makeResponse ok200 "text/plain" . DT.pack

instance ToResponse DT.Text where
  toResponse = makeResponse ok200 "text/plain"

instance (ToJSON a) => ToResponse (Json a) where
  toResponse (Json obj) = toResponse (toJSON obj)

instance ToResponse NW.Response where
  toResponse = id

instance (ToResponse b) => ToResponse (b, Status) where
  toResponse (b, status) = toResponse (b, status, mempty :: HeaderMap)

instance (ToResponse b) => ToResponse (b, Status, HeaderMap) where
  toResponse (b, status, hm) =
        toResponse b
      & mapResponseStatus (const status)
      & mapResponseHeaders (++ fromHeaderMap hm)

makeResponse :: Status -> ContentType -> DT.Text -> NW.Response
makeResponse status contentType body =
  NW.responseLBS status [makeHeader "Content-Type" contentType] (toLazyByteString body)

respond :: ToResponse a => a -> App ()
respond =  throwError . toResponse

respondWith :: ToResponse a => Handler a -> App ()
respondWith handler = runHandler handler >>= respond