{-# LANGUAGE OverloadedStrings #-}

module Main where

import           Test.Tasty              (TestTree, testGroup, defaultMain, after, DependencyType(..))
import           Test.Tasty.HUnit        (testCase)
import           Test.Tasty.QuickCheck   (testProperty, QuickCheckTests(..), QuickCheckVerbose(..))
import           Test.HUnit              (Assertion, (@?=))
import qualified Test.QuickCheck         as QC
import qualified Test.QuickCheck.Monadic as QC
import           Data.Aeson              (Value(..))
import           Control.Monad.IO.Class  (liftIO)
import           Control.Monad.Catch     (SomeException, MonadCatch(..))
import           Data.Text               (pack)
import qualified Data.HashMap.Strict            as HM
import qualified Data.Vector             as V
import           Quickjs


eval_1_plus_2 :: Assertion
eval_1_plus_2 = quickjsMultithreaded $ do
  v <- eval "1 + 2;"
  liftIO $ v @?= Number 3

genText = do 
  k <- QC.choose (0,200) 
  pack <$> QC.vectorOf k (QC.oneof $ map pure $ ['0'..'~'])

genVal 0 = QC.oneof
  [ 
    String <$> genText
  , Number . fromInteger <$> QC.arbitrary
  , Bool <$> QC.arbitrary
  , pure Null
  ]
genVal n | n > 0 = QC.oneof
  [ 
    do { k <- QC.choose (0,n) ; Object . HM.fromList <$> (zip <$> QC.vectorOf k genText <*> QC.vectorOf k genVal') }      
  , do { k <- QC.choose (0,n) ; Array . V.fromList <$> QC.vectorOf k genVal' }
  , String <$> genText
  , Number . fromInteger <$> QC.arbitrary
  , Bool <$> QC.arbitrary
  , pure Null
  ]
  where genVal' = genVal (n `div` 2)

instance QC.Arbitrary Value where
  arbitrary = QC.sized genVal




marshall_to_from_JSValue :: Value -> QC.Property    
marshall_to_from_JSValue val = QC.monadicIO $ do
  val' <- QC.run $ quickjsMultithreaded $ withJSValue val $ \jsval ->
    fromJSValue_ jsval
  QC.assert $ val == val'

tests :: TestTree
tests = 
  -- adjustOption (\_ -> QuickCheckTests 10) $
  -- adjustOption (\_ -> QuickCheckVerbose True) $  
  testGroup "Quickjs"
    [ testCase "empty quickjs call" (quickjsMultithreaded $ pure ())
    , testCase "eval '1 + 2;'" eval_1_plus_2
    , testProperty "marshalling Value to JSValue and back" marshall_to_from_JSValue
    ]

main = defaultMain tests