{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}

import Test.QuickCheck
import Data.Proxy
import Data.Word
import Data.Int
import Control.Monad
import Data.Primitive
import Data.Foldable
import Data.Monoid (Sum)
import Foreign.Storable
import Data.Aeson (ToJSON,FromJSON)

import Test.QuickCheck.Classes

main :: IO ()
main = do
  putStrLn "Testing properties for common typeclasses"
  r <- flip foldlMapM allPropsApplied $ \(typeName,properties) -> do
    putStrLn $ "------------"
    putStrLn $ "-- " ++ typeName
    putStrLn $ "------------"
    flip foldlMapM properties $ \(name,p) -> do
      putStrLn name
      r <- quickCheckResult p
      return $ case r of
        Success _ _ _ -> Good
        _ -> Bad
  putStrLn ""
  case r of
    Good -> putStrLn "All tests succeeded"
    Bad -> putStrLn "One or more tests failed"

data Status = Bad | Good

instance Monoid Status where
  mempty = Good
  mappend Good x = x
  mappend Bad _ = Bad

allPropsApplied :: [(String,[(String,Property)])]
allPropsApplied = 
  [ ("Word8",allProps (Proxy :: Proxy Word8))
  , ("Int16",allProps (Proxy :: Proxy Int16))
  , ("Int",allProps (Proxy :: Proxy Int))
  , ("Word",allProps (Proxy :: Proxy Word))
  , ("Int64",allProps (Proxy :: Proxy Int64))
  ]

allProps :: forall a. (Num a, Prim a, Storable a, Eq a, Arbitrary a, Show a, Read a, ToJSON a, FromJSON a) => Proxy a -> [(String,Property)]
allProps p = concat
  [ primProps p
  , storableProps p
  , monoidProps (Proxy :: Proxy (Sum a))
  , showReadProps p
  , jsonProps p
  ]

foldlMapM :: (Foldable t, Monoid b, Monad m) => (a -> m b) -> t a -> m b
foldlMapM f = foldlM (\b a -> fmap (mappend b) (f a)) mempty