{-# OPTIONS_GHC -Wno-orphans #-}

module Main
  ( main
  ) where

import           Control.Monad ( replicateM )
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as BC
import           Data.PEM ( PEM (..), pemParseBS, pemWriteBS )
import           Test.Framework ( Test, defaultMain, testGroup )
import           Test.Framework.Providers.QuickCheck2 ( testProperty )
import           Test.Framework.Providers.HUnit ( testCase )
import           Test.HUnit ( (@=?) )
import           Test.QuickCheck ( Arbitrary (..), Gen, choose, elements )

main :: IO ()
main = defaultMain tests

tests :: [Test]
tests =
  [ testGroup "units" testUnits
  , testDecodingMultiple
  , testUnmatchingNames
  , testProperty "marshall" testMarshall
  ]

testUnits :: [Test]
testUnits = zipWith
  (curry (\(i, (p, bs)) -> testCase (show i) (pemWriteBS p @=? BC.pack bs)))
  [ 0 :: Int .. ]
  [ (p1, bp1), (p2, bp2) ]
 where
  p1  = PEM { pemName = "abc", pemHeader = [], pemContent = B.replicate 64 0 }
  bp1 = unlines
    [ "-----BEGIN abc-----"
    , "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    , "AAAAAAAAAAAAAAAAAAAAAA=="
    , "-----END abc-----"
    ]
  p2 = PEM { pemName = "xxx", pemHeader = [], pemContent = B.replicate 12 3 }
  bp2 = unlines
    [ "-----BEGIN xxx-----"
    , "AwMDAwMDAwMDAwMD"
    , "-----END xxx-----"
    ]

testDecodingMultiple :: Test
testDecodingMultiple =
  testCase "multiple pems" (pemParseBS content @=? Right expected)
 where
  expected =
    [ PEM { pemName = "marker", pemHeader = [], pemContent = B.replicate 12 3 }
    , PEM { pemName = "marker2", pemHeader = [], pemContent = B.replicate 64 0 }
    ]
  content = BC.pack $ unlines
    [ "some text that is not related to PEM"
    , "and is just going to be ignored by the PEM parser."
    , ""
    , "even empty lines should be skip until the rightful marker"
    , "-----BEGIN marker-----"
    , "AwMDAwMDAwMDAwMD"
    , "-----END marker-----"
    , "some middle text"
    , "-----BEGIN marker2-----"
    , "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    , "AAAAAAAAAAAAAAAAAAAAAA=="
    , "-----END marker2-----"
    , "and finally some trailing text."
    ]

testUnmatchingNames :: Test
testUnmatchingNames =
  testCase "unmatching name"
    ( let r = pemParseBS content
      in  case r of
            Left _ -> True @=? True
            _      -> r @=? Left ""
    )
 where
  content = BC.pack $ unlines
    [ "-----BEGIN marker-----"
    , "AAAA"
    , "-----END marker2-----"
    ]

testMarshall :: [PEM] -> Bool
testMarshall pems = readPems == Right pems
 where
  readPems = pemParseBS writtenPems
  writtenPems = B.concat (map pemWriteBS pems)

arbitraryName :: Gen [Char]
arbitraryName = choose (1, 30) >>= \i -> replicateM i arbitraryAscii
 where
  arbitraryAscii = elements ['A'..'Z']

arbitraryContent :: Gen B.ByteString
arbitraryContent = choose (1,100) >>= \i ->
  (B.pack . map fromIntegral) `fmap` replicateM i (choose (0,255) :: Gen Int)

instance Arbitrary PEM where
  arbitrary = PEM <$> arbitraryName <*> pure [] <*> arbitraryContent
