{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE RankNTypes            #-}
{-|
Module      : HsLua.Class.Invokable
Copyright   : © 2007–2012 Gracjan Polak,
                2012–2016 Ömer Sinan Ağacan,
                2017-2023 Albert Krewinkel
License     : MIT
Maintainer  : Albert Krewinkel <tarleb@hslua.org>
Stability   : beta
Portability : FlexibleInstances, ForeignFunctionInterface, ScopedTypeVariables

Call Lua functions from Haskell.
-}
module HsLua.Class.Invokable
  ( Invokable (..)
  , invoke
  ) where

import Data.ByteString (append)
import HsLua.Core as Lua
import HsLua.Class.Peekable
import HsLua.Class.Pushable
import HsLua.Class.Util (popValue)

-- | Helper class used to make Lua functions useable from Haskell.
class Invokable a where
  addArg :: Name -> (forall e. LuaError e => LuaE e ()) -> NumArgs -> a

instance (LuaError e, Peekable a) => Invokable (LuaE e a) where
  addArg :: Name -> (forall e. LuaError e => LuaE e ()) -> NumArgs -> LuaE e a
addArg Name
fnName forall e. LuaError e => LuaE e ()
pushArgs NumArgs
nargs = do
    Status
_ <- forall e. ByteString -> LuaE e Status
dostring forall a b. (a -> b) -> a -> b
$ ByteString
"return " ByteString -> ByteString -> ByteString
`append` Name -> ByteString
Lua.fromName Name
fnName
    forall e. LuaError e => LuaE e ()
pushArgs
    forall e. LuaError e => NumArgs -> NumResults -> LuaE e ()
call NumArgs
nargs NumResults
1
    forall e a. (LuaError e, Peekable a) => LuaE e a
popValue

instance (Pushable a, Invokable b) => Invokable (a -> b) where
  addArg :: Name -> (forall e. LuaError e => LuaE e ()) -> NumArgs -> a -> b
addArg Name
fnName forall e. LuaError e => LuaE e ()
pushArgs NumArgs
nargs a
x =
    forall a.
Invokable a =>
Name -> (forall e. LuaError e => LuaE e ()) -> NumArgs -> a
addArg Name
fnName (forall e. LuaError e => LuaE e ()
pushArgs forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> forall a e. (Pushable a, LuaError e) => a -> LuaE e ()
push a
x) (NumArgs
nargs forall a. Num a => a -> a -> a
+ NumArgs
1)

-- | Invoke a Lua function. Use as:
--
-- > v <- invoke "proc" "abc" (1::Int) (5.0::Double)
invoke :: Invokable a => Name -> a
invoke :: forall a. Invokable a => Name -> a
invoke Name
fname = forall a.
Invokable a =>
Name -> (forall e. LuaError e => LuaE e ()) -> NumArgs -> a
addArg Name
fname (forall (m :: * -> *) a. Monad m => a -> m a
return ()) NumArgs
0