{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
module Data.Aeson.Encoding.Internal
    (
    
      Encoding' (..)
    , Encoding
    , encodingToLazyByteString
    , unsafeToEncoding
    , retagEncoding
    , Series (..)
    , pairs
    , pair
    , pairStr
    , pair'
    
    , nullEncoding
    
    , emptyArray_
    , emptyObject_
    , wrapObject
    , wrapArray
    , null_
    , bool
    , text
    , lazyText
    , string
    , list
    , dict
    , tuple
    , (>*<)
    , InArray
    , empty
    , (><)
    , econcat
    
    , int8, int16, int32, int64, int
    , word8, word16, word32, word64, word
    , integer, float, double, scientific
    
    , int8Text, int16Text, int32Text, int64Text, intText
    , word8Text, word16Text, word32Text, word64Text, wordText
    , integerText, floatText, doubleText, scientificText
    
    , day
    , localTime
    , utcTime
    , timeOfDay
    , zonedTime
    
    , value
    
    , comma, colon, openBracket, closeBracket, openCurly, closeCurly
    ) where
import Prelude ()
import Prelude.Compat
import Data.Aeson.Types.Internal (Value)
import Data.ByteString.Builder (Builder, char7, toLazyByteString)
import Data.Int
import Data.Scientific (Scientific)
import Data.Semigroup (Semigroup ((<>)))
import Data.Text (Text)
import Data.Time (Day, LocalTime, TimeOfDay, UTCTime, ZonedTime)
import Data.Typeable (Typeable)
import Data.Word
import qualified Data.Aeson.Encoding.Builder as EB
import qualified Data.ByteString.Builder as B
import qualified Data.ByteString.Lazy as BSL
import qualified Data.Text.Lazy as LT
newtype Encoding' tag = Encoding {
      fromEncoding :: Builder
      
    } deriving (Typeable)
type Encoding = Encoding' Value
unsafeToEncoding :: Builder -> Encoding' a
unsafeToEncoding = Encoding
encodingToLazyByteString :: Encoding' a -> BSL.ByteString
encodingToLazyByteString = toLazyByteString . fromEncoding
{-# INLINE encodingToLazyByteString #-}
retagEncoding :: Encoding' a -> Encoding' b
retagEncoding = Encoding . fromEncoding
instance Show (Encoding' a) where
    show (Encoding e) = show (toLazyByteString e)
instance Eq (Encoding' a) where
    Encoding a == Encoding b = toLazyByteString a == toLazyByteString b
instance Ord (Encoding' a) where
    compare (Encoding a) (Encoding b) =
      compare (toLazyByteString a) (toLazyByteString b)
data Series = Empty
            | Value (Encoding' Series)
            deriving (Typeable)
pair :: Text -> Encoding -> Series
pair name val = pair' (text name) val
{-# INLINE pair #-}
pairStr :: String -> Encoding -> Series
pairStr name val = pair' (string name) val
{-# INLINE pairStr #-}
pair' :: Encoding' Text -> Encoding -> Series
pair' name val = Value $ retagEncoding $ retagEncoding name >< colon >< val
instance Semigroup Series where
    Empty   <> a       = a
    a       <> Empty   = a
    Value a <> Value b = Value (a >< comma >< b)
instance Monoid Series where
    mempty  = Empty
    mappend = (<>)
nullEncoding :: Encoding' a -> Bool
nullEncoding = BSL.null . toLazyByteString . fromEncoding
emptyArray_ :: Encoding
emptyArray_ = Encoding EB.emptyArray_
emptyObject_ :: Encoding
emptyObject_ = Encoding EB.emptyObject_
wrapArray :: Encoding' a -> Encoding
wrapArray e = retagEncoding $ openBracket >< e >< closeBracket
wrapObject :: Encoding' a -> Encoding
wrapObject e = retagEncoding $ openCurly >< e >< closeCurly
null_ :: Encoding
null_ = Encoding EB.null_
bool :: Bool -> Encoding
bool True = Encoding "true"
bool False = Encoding "false"
pairs :: Series -> Encoding
pairs (Value v) = openCurly >< retagEncoding v >< closeCurly
pairs Empty     = emptyObject_
{-# INLINE pairs #-}
list :: (a -> Encoding) -> [a] -> Encoding
list _  []     = emptyArray_
list to' (x:xs) = openBracket >< to' x >< commas xs >< closeBracket
  where
    commas = foldr (\v vs -> comma >< to' v >< vs) empty
{-# INLINE list #-}
dict
    :: (k -> Encoding' Text)                     
    -> (v -> Encoding)                                
    -> (forall a. (k -> v -> a -> a) -> a -> m -> a)  
    -> m                                              
    -> Encoding
dict encodeKey encodeVal foldrWithKey = pairs . foldrWithKey go mempty
  where
    go k v c = Value (encodeKV k v) <> c
    encodeKV k v = retagEncoding (encodeKey k) >< colon >< retagEncoding (encodeVal v)
{-# INLINE dict #-}
data InArray
infixr 6 >*<
(>*<) :: Encoding' a -> Encoding' b -> Encoding' InArray
a >*< b = retagEncoding a >< comma >< retagEncoding b
{-# INLINE (>*<) #-}
empty :: Encoding' a
empty = Encoding mempty
econcat :: [Encoding' a] -> Encoding' a
econcat = foldr (><) empty
infixr 6 ><
(><) :: Encoding' a -> Encoding' a -> Encoding' a
Encoding a >< Encoding b = Encoding (a <> b)
{-# INLINE (><) #-}
tuple :: Encoding' InArray -> Encoding
tuple b = retagEncoding $ openBracket >< b >< closeBracket
{-# INLINE tuple #-}
text :: Text -> Encoding' a
text = Encoding . EB.text
lazyText :: LT.Text -> Encoding' a
lazyText t = Encoding $
    B.char7 '"' <>
    LT.foldrChunks (\x xs -> EB.unquoted x <> xs) (B.char7 '"') t
string :: String -> Encoding' a
string = Encoding . EB.string
comma, colon, openBracket, closeBracket, openCurly, closeCurly :: Encoding' a
comma        = Encoding $ char7 ','
colon        = Encoding $ char7 ':'
openBracket  = Encoding $ char7 '['
closeBracket = Encoding $ char7 ']'
openCurly    = Encoding $ char7 '{'
closeCurly   = Encoding $ char7 '}'
int8 :: Int8 -> Encoding
int8 = Encoding . B.int8Dec
int16 :: Int16 -> Encoding
int16 = Encoding . B.int16Dec
int32 :: Int32 -> Encoding
int32 = Encoding . B.int32Dec
int64 :: Int64 -> Encoding
int64 = Encoding . B.int64Dec
int :: Int -> Encoding
int = Encoding . B.intDec
word8 :: Word8 -> Encoding
word8 = Encoding . B.word8Dec
word16 :: Word16 -> Encoding
word16 = Encoding . B.word16Dec
word32 :: Word32 -> Encoding
word32 = Encoding . B.word32Dec
word64 :: Word64 -> Encoding
word64 = Encoding . B.word64Dec
word :: Word -> Encoding
word = Encoding . B.wordDec
integer :: Integer -> Encoding
integer = Encoding . B.integerDec
float :: Float -> Encoding
float = realFloatToEncoding $ Encoding . B.floatDec
double :: Double -> Encoding
double = realFloatToEncoding $ Encoding . B.doubleDec
scientific :: Scientific -> Encoding
scientific = Encoding . EB.scientific
realFloatToEncoding :: RealFloat a => (a -> Encoding) -> a -> Encoding
realFloatToEncoding e d
    | isNaN d || isInfinite d = null_
    | otherwise               = e d
{-# INLINE realFloatToEncoding #-}
int8Text :: Int8 -> Encoding' a
int8Text = Encoding . EB.quote . B.int8Dec
int16Text :: Int16 -> Encoding' a
int16Text = Encoding . EB.quote . B.int16Dec
int32Text :: Int32 -> Encoding' a
int32Text = Encoding . EB.quote . B.int32Dec
int64Text :: Int64 -> Encoding' a
int64Text = Encoding . EB.quote . B.int64Dec
intText :: Int -> Encoding' a
intText = Encoding . EB.quote . B.intDec
word8Text :: Word8 -> Encoding' a
word8Text = Encoding . EB.quote . B.word8Dec
word16Text :: Word16 -> Encoding' a
word16Text = Encoding . EB.quote . B.word16Dec
word32Text :: Word32 -> Encoding' a
word32Text = Encoding . EB.quote . B.word32Dec
word64Text :: Word64 -> Encoding' a
word64Text = Encoding . EB.quote . B.word64Dec
wordText :: Word -> Encoding' a
wordText = Encoding . EB.quote . B.wordDec
integerText :: Integer -> Encoding' a
integerText = Encoding . EB.quote . B.integerDec
floatText :: Float -> Encoding' a
floatText = Encoding . EB.quote . B.floatDec
doubleText :: Double -> Encoding' a
doubleText = Encoding . EB.quote . B.doubleDec
scientificText :: Scientific -> Encoding' a
scientificText = Encoding . EB.quote . EB.scientific
day :: Day -> Encoding' a
day = Encoding . EB.quote . EB.day
localTime :: LocalTime -> Encoding' a
localTime = Encoding . EB.quote . EB.localTime
utcTime :: UTCTime -> Encoding' a
utcTime = Encoding . EB.quote . EB.utcTime
timeOfDay :: TimeOfDay -> Encoding' a
timeOfDay = Encoding . EB.quote . EB.timeOfDay
zonedTime :: ZonedTime -> Encoding' a
zonedTime = Encoding . EB.quote . EB.zonedTime
value :: Value -> Encoding
value = Encoding . EB.encodeToBuilder