{-# LANGUAGE DeriveDataTypeable        #-}
{-# LANGUAGE ExistentialQuantification #-}

module Test.Convert (tests) where

import           Fay.Convert

import qualified Data.Aeson.Parser              as Aeson
import           Data.Attoparsec.ByteString
import qualified Data.ByteString                as Bytes
import qualified Data.ByteString.UTF8           as UTF8
import           Data.Data
import           Data.Text                      (Text, pack)
import           Test.Tasty
import           Test.Tasty.HUnit

tests :: TestTree
tests = testGroup "Test.Convert" [reading, showing]
  where reading = testGroup "reading" $
          flip map readTests $ \(ReadTest value) ->
            let label = show value
            in testCase label $
                 assertEqual label (Just value) (showToFay value >>= readFromFay)
        showing = testGroup "showing" $
          flip map showTests $ \(Testcase value output) ->
            let label = show value
            in testCase label $
                 assertEqual label
                   (either error Just $ parseOnly Aeson.value output)
                   (showToFay value)


--------------------------------------------------------------------------------
-- Test cases

-- | A test.
data Testcase = forall x. (Show x,Data x) => Testcase x Bytes.ByteString

-- | A read test.
data ReadTest = forall x. (Data x,Show x,Eq x,Read x) => ReadTest x


-- | Tests

-- | Read tests.
readTests :: [ReadTest]
readTests =
  [ReadTest $ NullaryConstructor
  ,ReadTest $ NAryConstructor 123 66.6
  ,ReadTest $ LabelledRecord { barInt = 123, barDouble = 66.6 }
  ,ReadTest $ LabelledRecord2 { bar = 123, bob = 66.6 }
  ,ReadTest $ FooBar "Tinkie Winkie" "Humanzee" Zot
  ,ReadTest $ Bar $ Foo "one" "two"
  ,ReadTest $ StepcutFoo 123
  ,ReadTest $ StepcutBar (StepcutFoo 456)
  ,ReadTest $ StepcutFoo' 789
  ,ReadTest $ Baz (StepcutFoo' 10112)
  ,ReadTest $ TextConstructor $ pack "This is \"some text\n\n\""
  ,ReadTest $ (("foo",'a') :: (String,Char))
  ,ReadTest $ ((pack "foo",'a',23) :: (Text,Char,Int))
  ,ReadTest $ TupleList [(pack "foo",pack "bar")]
  ,ReadTest $ TupleList' [((pack "foo",23) :: (Text,Int))]
  ,ReadTest $ ()
  ]

-- | Test cases.
showTests :: [Testcase]
showTests =
   -- Fundamental data types
  [(1 :: Int) → "1"
  ,(1 :: Float) → "1.0"
  ,(1/2 :: Float) → "0.5"
  ,(1 :: Double) → "1.0"
  ,(1/2 :: Double) → "0.5"
  ,([1,2] :: [Int]) → "[1,2]"
  ,((1,2) :: (Int,Int)) → "[1,2]"
  ,"abc" → "\"abc\""
  ,'a' → "\"a\""
  , () → "null"
  -- Data records
  ,NullaryConstructor → "{\"instance\":\"NullaryConstructor\"}"
  ,NAryConstructor 123 4.5 → "{\"slot1\":123,\"slot2\":4.5,\"instance\":\"NAryConstructor\"}"
  ,LabelledRecord { barInt = 123, barDouble = 4.5 }
     → "{\"barDouble\":4.5,\"barInt\":123,\"instance\":\"LabelledRecord\"}"
  ,Bar (Foo "one" "two") → "{\"slot1\":{\"slot1\":\"one\",\"slot2\":\"two\",\"instance\":\"Foo\"},\"instance\":\"Bar\"}"
  ,TextConstructor (pack "foo bar baz") → "{\"slot1\":\"foo bar baz\",\"instance\":\"TextConstructor\"}"
  -- Unicode
  ,"¡ ¢ £ ¤ ¥ " → "\"¡ ¢ £ ¤ ¥ \""
  ,"Ā ā Ă ă Ą " → "\"Ā ā Ă ă Ą \""
  ,"ƀ Ɓ Ƃ ƃ Ƅ " → "\"ƀ Ɓ Ƃ ƃ Ƅ \""
  ,"ɐ ɑ ɒ ɓ ɔ " → "\"ɐ ɑ ɒ ɓ ɔ \""
  ,"Ё Ђ Ѓ Є Ѕ " → "\"Ё Ђ Ѓ Є Ѕ \""
  ,"Ա Բ Գ Դ Ե " → "\"Ա Բ Գ Դ Ե \""
  ,"، ؛ ؟ ء آ " → "\"، ؛ ؟ ء آ \""
  ,"ँ ं ः अ आ " → "\"ँ ं ः अ आ \""
  ,"ఁ ం ః అ ఆ " → "\"ఁ ం ః అ ఆ \""
  ,"ก ข ฃ ค ฅ " → "\"ก ข ฃ ค ฅ \""
  ,"ກ ຂ ຄ ງ ຈ " → "\"ກ ຂ ຄ ງ ຈ \""
  ,"ༀ ༁ ༂ ༃ ༄ " → "\"ༀ ༁ ༂ ༃ ༄ \""
  ,"Ⴀ Ⴁ Ⴂ Ⴃ Ⴄ " → "\"Ⴀ Ⴁ Ⴂ Ⴃ Ⴄ \""
  ,"Ḁ ḁ Ḃ ḃ Ḅ " → "\"Ḁ ḁ Ḃ ḃ Ḅ \""
  ,"ぁ あ ぃ い ぅ " → "\"ぁ あ ぃ い ぅ \""
  ,"ァ ア ィ イ ゥ " → "\"ァ ア ィ イ ゥ \""
  ,"ㄅ ㄆ ㄇ ㄈ ㄉ " → "\"ㄅ ㄆ ㄇ ㄈ ㄉ \""
  ,"ㄱ ㄲ ㄳ ㄴ ㄵ " → "\"ㄱ ㄲ ㄳ ㄴ ㄵ \""
  ]

  where x → y = Testcase x (UTF8.fromString y)

data Foo = Foo String String
  deriving (Show,Data,Typeable,Read,Eq)
data Bar = Bar Foo
  deriving (Show,Data,Typeable,Read,Eq)

-- | Nullary constructor.
data NullaryConstructor = NullaryConstructor
  deriving (Show,Data,Typeable,Read,Eq)

-- | n-ary labelless constructor.
data NAryConstructor = NAryConstructor Int Double
  deriving (Show,Data,Typeable,Read,Eq)

-- | Labelled record.
data LabelledRecord = LabelledRecord { barInt :: Int, barDouble :: Double }
                    | LabelledRecord2 { bar :: Int, bob :: Float }
  deriving (Show,Data,Typeable,Read,Eq)

-- | Order matters in unlabelled constructors.
data SomeThing =
  FooBar String String Zot
  deriving (Read,Data,Typeable,Show,Eq)

-- | This triggers order difference. Go figure.
data Zot = Zot
  deriving (Read,Data,Typeable,Show,Eq)

data StepcutFoo = StepcutFoo { _unStepcutFoo :: Int }
    deriving (Eq, Show, Read, Typeable, Data)

data StepcutBar = StepcutBar StepcutFoo
    deriving (Eq, Show, Read, Typeable, Data)

data StepcutFoo' = StepcutFoo' Int
    deriving (Eq, Show, Read, Typeable, Data)

data Baz = Baz StepcutFoo'
    deriving (Eq, Show, Read, Typeable, Data)

data TextConstructor = TextConstructor Text
    deriving (Eq, Show, Read, Typeable, Data)

data TupleList = TupleList [(Text,Text)]
  deriving (Read, Typeable, Data, Show, Eq)

data TupleList' a = TupleList' [(Text,a)]
  deriving (Read, Typeable, Data, Show, Eq)
