{-# LANGUAGE FlexibleContexts          #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE OverloadedStrings         #-}
module Encoder
  ( encoderTests
  , encodeImage
  , testImageDataType
  ) where

import           Control.Lens          ((<&>), (?~))

import           Test.Tasty            (TestName, TestTree, testGroup)
import           Test.Tasty.HUnit      (Assertion, testCase, (@?=))

import           Data.Proxy            (Proxy (..))

import           Waargonaut.Encode     (Encoder, Encoder')
import qualified Waargonaut.Encode     as E

import           Data.Text.Lazy  (Text)

import           Types.Common          (Image (..), Overlayed (..), testFudge,
                                        testImageDataType)

import           Waargonaut.Generic    (GWaarg, mkEncoder, proxy)
import           Waargonaut.Types.Json (oat)

testOptionalKeyValue :: Assertion
testOptionalKeyValue = do
  let
    encoder = E.mapLikeObj $ \(a,b) ->
      E.atKey' "A" E.text a .
      E.atOptKey' "B" E.int b

    hasKey = ("bob", Just 33)
    withKey = "{\"A\":\"bob\",\"B\":33}"

    noKey = ("bob", Nothing)
    withoutKey = "{\"A\":\"bob\"}"

  E.simplePureEncodeTextNoSpaces encoder hasKey @?= withKey
  E.simplePureEncodeTextNoSpaces encoder noKey @?= withoutKey

testImageEncodedNoSpaces :: Text
testImageEncodedNoSpaces = "{\"Width\":800,\"Height\":600,\"Title\":\"View from 15th Floor\",\"Animated\":false,\"IDs\":[116,943,234,38793]}"

-- | The recommended way of defining an Encoder is to be explicit.
encodeImage :: Applicative f => Encoder f Image
encodeImage = E.mapLikeObj $ \img ->
    E.intAt "Width" (_imageWidth img)
  . E.intAt "Height" (_imageHeight img)
  . E.textAt "Title" (_imageTitle img)
  . E.boolAt "Animated" (_imageAnimated img)
  . E.listAt E.int "IDs" (_imageIDs img)

testFudgeEncodedWithConsName :: Text
testFudgeEncodedWithConsName = "{\"fudgey\":\"Chocolate\"}"

testOverlayed :: Overlayed
testOverlayed = Overlayed "fred" testFudge

testOverlayedOut :: Text
testOverlayedOut = "{\"id\":\"fred\",\"fudgey\":\"Chocolate\"}"

encodeOverlay :: Applicative f => Encoder f Overlayed
encodeOverlay = E.encodeA $ \(Overlayed i f) -> E.runEncoder fudgeEnc f
  <&> oat "id" ?~ E.runPureEncoder E.text i
  where
    fudgeEnc = proxy mkEncoder (Proxy :: Proxy GWaarg)

tCase
  :: TestName
  -> Encoder' a
  -> a
  -> Text
  -> TestTree
tCase nm enc a expected = testCase nm $
  E.simplePureEncodeTextNoSpaces enc a @?= expected

encoderTests :: TestTree
encoderTests = testGroup "Encoder"
  [ tCase "Image" encodeImage testImageDataType testImageEncodedNoSpaces
  , tCase "Image (Generic)" enc testImageDataType testImageEncodedNoSpaces
  , tCase "newtype - with constructor name" enc testFudge testFudgeEncodedWithConsName
  , tCase "Overlayed" encodeOverlay testOverlayed testOverlayedOut
  , testCase "Optional Key:Value" testOptionalKeyValue
  ]
  where
    enc = proxy mkEncoder (Proxy :: Proxy GWaarg)
