    {-# LANGUAGE RankNTypes #-}
    {-|
    Module      : HsLua.Core.Userdata
    Copyright   : © 2007–2012 Gracjan Polak;
                  © 2012–2016 Ömer Sinan Ağacan;
                  © 2017-2024 Albert Krewinkel
    License     : MIT
    Maintainer  : Albert Krewinkel <tarleb@hslua.org>
    Stability   : beta
    Portability : non-portable (depends on GHC)

    Convenience functions to convert Haskell values into Lua userdata.
    -}
    module HsLua.Core.Userdata
      ( newhsuserdatauv
      , newudmetatable
      , fromuserdata
      , putuserdata
      ) where

    import HsLua.Core.Types (LuaE, Name (..), StackIndex, liftLua, fromLuaBool)
    import Lua.Userdata
      ( hslua_fromuserdata
      , hslua_newhsuserdatauv
      , hslua_newudmetatable
      , hslua_putuserdata
      )
    import qualified Data.ByteString as B

    -- | Creates a new userdata wrapping the given Haskell object. The
    -- userdata is pushed to the top of the stack.
    newhsuserdatauv :: forall a e. a -- ^ Haskell object
                    -> Int           -- ^ number of extra userdata values
                    -> LuaE e ()
    newhsuserdatauv :: forall a e. a -> Int -> LuaE e ()
    newhsuserdatauv a
    x Int
    nuvalue = (State -> IO ()) -> LuaE e ()
    forall a e. (State -> IO a) -> LuaE e a
    liftLua ((State -> IO ()) -> LuaE e ()) -> (State -> IO ()) -> LuaE e ()
    forall a b. (a -> b) -> a -> b
    $ \State
    l ->
      State -> a -> CInt -> IO ()
    forall a. State -> a -> CInt -> IO ()
    hslua_newhsuserdatauv State
    l a
    x (Int -> CInt
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int
    nuvalue)
    {-# INLINABLE newhsuserdatauv #-}

    -- | Creates and registers a new metatable for a userdata-wrapped
    -- Haskell value; checks whether a metatable of that name has been
    -- registered yet and uses the registered table if possible.
    --
    -- Returns 'True' if a new metatable was created, and 'False' otherwise.
    --
    -- Using a metatable created by this functions ensures that the pointer
    -- to the Haskell value will be freed when the userdata object is
    -- garbage collected in Lua.
    --
    -- The name may not contain a nul character.
    newudmetatable :: Name -> LuaE e Bool
    newudmetatable :: forall e. Name -> LuaE e Bool
    newudmetatable (Name ByteString
    name) = (State -> IO Bool) -> LuaE e Bool
    forall a e. (State -> IO a) -> LuaE e a
    liftLua ((State -> IO Bool) -> LuaE e Bool)
    -> (State -> IO Bool) -> LuaE e Bool
    forall a b. (a -> b) -> a -> b
    $ \State
    l ->
      ByteString -> (CString -> IO Bool) -> IO Bool
    forall a. ByteString -> (CString -> IO a) -> IO a
    B.useAsCString ByteString
    name ((LuaBool -> Bool) -> IO LuaBool -> IO Bool
    forall a b. (a -> b) -> IO a -> IO b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap LuaBool -> Bool
    fromLuaBool (IO LuaBool -> IO Bool)
    -> (CString -> IO LuaBool) -> CString -> IO Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . State -> CString -> IO LuaBool
    hslua_newudmetatable State
    l)
    {-# INLINABLE newudmetatable #-}

    -- | Retrieves a Haskell object from userdata at the given index. The
    -- userdata /must/ have the given name.
    fromuserdata :: forall a e.
                    StackIndex  -- ^ stack index of userdata
                 -> Name        -- ^ expected name of userdata object
                 -> LuaE e (Maybe a)
    fromuserdata :: forall a e. StackIndex -> Name -> LuaE e (Maybe a)
    fromuserdata StackIndex
    idx (Name ByteString
    name) = (State -> IO (Maybe a)) -> LuaE e (Maybe a)
    forall a e. (State -> IO a) -> LuaE e a
    liftLua ((State -> IO (Maybe a)) -> LuaE e (Maybe a))
    -> (State -> IO (Maybe a)) -> LuaE e (Maybe a)
    forall a b. (a -> b) -> a -> b
    $ \State
    l ->
      ByteString -> (CString -> IO (Maybe a)) -> IO (Maybe a)
    forall a. ByteString -> (CString -> IO a) -> IO a
    B.useAsCString ByteString
    name (State -> StackIndex -> CString -> IO (Maybe a)
    forall a. State -> StackIndex -> CString -> IO (Maybe a)
    hslua_fromuserdata State
    l StackIndex
    idx)
    {-# INLINABLE fromuserdata #-}

    -- | Replaces the Haskell value contained in the userdata value at
    -- @index@. Checks that the userdata is of type @name@ and returns
    -- 'True' on success, or 'False' otherwise.
    putuserdata :: forall a e.
                   StackIndex   -- ^ index
                -> Name         -- ^ name
                -> a            -- ^ new value
                -> LuaE e Bool
    putuserdata :: forall a e. StackIndex -> Name -> a -> LuaE e Bool
    putuserdata StackIndex
    idx (Name ByteString
    name) a
    x = (State -> IO Bool) -> LuaE e Bool
    forall a e. (State -> IO a) -> LuaE e a
    liftLua ((State -> IO Bool) -> LuaE e Bool)
    -> (State -> IO Bool) -> LuaE e Bool
    forall a b. (a -> b) -> a -> b
    $ \State
    l ->
      ByteString -> (CString -> IO Bool) -> IO Bool
    forall a. ByteString -> (CString -> IO a) -> IO a
    B.useAsCString ByteString
    name ((CString -> IO Bool) -> IO Bool)
    -> (CString -> IO Bool) -> IO Bool
    forall a b. (a -> b) -> a -> b
    $ \CString
    namePtr ->
      State -> StackIndex -> CString -> a -> IO Bool
    forall a. State -> StackIndex -> CString -> a -> IO Bool
    hslua_putuserdata State
    l StackIndex
    idx CString
    namePtr a
    x
    {-# INLINABLE putuserdata #-}