content: {-# LANGUAGE OverloadedStrings   #-}
  {-# LANGUAGE ScopedTypeVariables #-}
  {-# LANGUAGE TypeApplications    #-}
  {-|
  Module      : Test.Tasty.Lua.Arbitrary
  Copyright   : © 2019-2024 Albert Krewinkel
  License     : MIT
  Maintainer  : Albert Krewinkel <tarleb@hslua.org>
  
  Generators for arbitrary Lua values.
  -}
  module Test.Tasty.Lua.Arbitrary
    ( registerArbitrary
    , registerDefaultGenerators
    , pushArbitraryTable
    )
  where
  
  import HsLua.Core
  import HsLua.Marshalling
  import Lua.Arbitrary ()
  import Test.QuickCheck (Arbitrary (..), generate, vectorOf)
  
  -- | Register a Lua value generator.
  registerArbitrary :: forall a e. (Arbitrary a, LuaError e)
                    => Name
                    -> Pusher e a
                    -> Peeker e a
                    -> LuaE e ()
  registerArbitrary name push peek = do
    pushArbitraryTable
    pushName name
    newtable
    pushName "generator"
    pushHaskellFunction $ do
      samples <- liftIO (generate $ vectorOf 30 (arbitrary @a))
      pushIterator (\x -> NumResults 1 <$ push x) samples
    rawset (nth 3)
    pushName "shrink"
    pushHaskellFunction $
      runPeeker peek (nthBottom 1) >>= \case
        Success x -> do
          pushList push (shrink x)
          pure (NumResults 1)
        _ -> pure (NumResults 0)
    rawset (nth 3)
    rawset (nth 3)
    pop 1  -- remove `tasty.arbitrary` table
  
  
  -- | Pushes the table holding all arbitrary generators to the stack.
  pushArbitraryTable :: LuaE e ()
  pushArbitraryTable =
    newmetatable "tasty.arbitrary" >>= \case
      False ->    -- table exists
        pure ()
      True  -> do -- table created
        -- make table it's own metatable
        pushvalue top
        setmetatable (nth 2)
  
  registerDefaultGenerators :: LuaError e => LuaE e ()
  registerDefaultGenerators = do
    registerArbitrary "boolean" pushboolean peekBool
    registerArbitrary "integer" pushinteger peekIntegral
    registerArbitrary "number"  pushnumber  peekRealFloat
    registerArbitrary "string"  pushString  peekString
  