{-# OPTIONS_GHC -fno-warn-orphans #-}

module AesonQuickCheck where

import Data.Aeson
import Test.QuickCheck
import           Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Vector as V
import           Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap
import Data.Scientific
import Data.Hashable (Hashable)
import Control.Applicative


newtype ArbObject = ArbObject { unArbObject :: Object }

-- bleh hack
instance Arbitrary Text where
    arbitrary = fmap T.pack $ arbitrary
    shrink txt = fmap T.pack $ shrink $ T.unpack txt

arbitraryArray :: (Arbitrary a) => Gen [a]
arbitraryArray = sized $ \s -> do
    (Positive len) <- arbitrary
    vector $ min s (len `mod` 10)

arbitraryObject :: (Hashable k, Eq k, Arbitrary k, Arbitrary v) => Gen (HashMap k v)
arbitraryObject = fmap HashMap.fromList arbitraryArray

shrinkHashMap hashMap = fmap HashMap.fromList (shrink (HashMap.toList hashMap))

instance Arbitrary ArbObject where
    arbitrary = sized $ \s -> let s' = max 1 (s `div` 10) in
        ArbObject <$> resize s' arbitraryObject

instance Arbitrary Value where
    arbitrary = sized $ \s -> let s' = max 1 (s - 1) in seq s' $ resize s' $ oneof
        [ Object . unArbObject <$> arbitrary
        , Array . V.fromList <$> arbitraryArray
        , String <$> arbitrary
        , Number <$> arbitrary
        , Bool   <$> arbitrary
        , return Null
        ]

    shrink e = case e of
        Object x -> Object <$> shrinkHashMap x
        Array x  -> Array <$> shrink x
        String _ -> []
        Number _ -> []
        Bool _   -> []
        Null     -> []

instance Arbitrary Scientific where
    arbitrary = do
        val <- arbitrary :: Gen Int
        return $ scientific (fromIntegral val) 1
    shrink val = do
        val' <- shrink (floor val)
        return $ scientific val' 0

instance Arbitrary a => Arbitrary (V.Vector a) where
    arbitrary = V.fromList <$> arbitrary
    shrink = fmap V.fromList . shrink . V.toList
