{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-|
Module      : Foreign.Lua.Types.Pushable
Copyright   : © 2007–2012 Gracjan Polak,
                2012–2016 Ömer Sinan Ağacan,
                2017-2020 Albert Krewinkel
License     : MIT
Maintainer  : Albert Krewinkel <tarleb+hslua@zeitkraut.de>
Stability   : beta
Portability : FlexibleInstances, ScopedTypeVariables

Sending haskell objects to the lua stack.
-}
module Foreign.Lua.Types.Pushable
  ( Pushable (..)
  , pushList
  ) where

import Data.ByteString (ByteString)
import Data.Map (Map)
import Data.Text (Text)
import Data.Set (Set)
import Foreign.Lua.Core as Lua
import Foreign.Lua.Push hiding (pushList)
import Foreign.Ptr (Ptr)

import qualified Data.ByteString.Lazy as BL
import qualified Foreign.Lua.Push as Push

-- | A value that can be pushed to the Lua stack.
class Pushable a where
  -- | Pushes a value onto Lua stack, casting it into meaningfully nearest Lua
  -- type.
  push :: a -> Lua ()

instance Pushable () where
  push :: () -> Lua ()
push = Lua () -> () -> Lua ()
forall a b. a -> b -> a
const Lua ()
pushnil

instance Pushable Lua.Integer where
  push :: Integer -> Lua ()
push = Integer -> Lua ()
pushinteger

instance Pushable Lua.Number where
  push :: Number -> Lua ()
push = Number -> Lua ()
pushnumber

instance Pushable ByteString where
  push :: ByteString -> Lua ()
push = ByteString -> Lua ()
pushstring

instance Pushable Bool where
  push :: Bool -> Lua ()
push = Bool -> Lua ()
pushboolean

instance Pushable CFunction where
  push :: CFunction -> Lua ()
push = CFunction -> Lua ()
pushcfunction

instance Pushable (Ptr a) where
  push :: Ptr a -> Lua ()
push = Ptr a -> Lua ()
forall a. Ptr a -> Lua ()
pushlightuserdata

instance Pushable Text where
  push :: Text -> Lua ()
push = Text -> Lua ()
pushText

instance Pushable BL.ByteString where
  push :: ByteString -> Lua ()
push = ByteString -> Lua ()
pushLazyByteString

instance Pushable Prelude.Integer where
  push :: Integer -> Lua ()
push = Integer -> Lua ()
forall a. (Integral a, Show a) => a -> Lua ()
pushIntegral

instance Pushable Int where
  push :: Int -> Lua ()
push = Int -> Lua ()
forall a. (Integral a, Show a) => a -> Lua ()
pushIntegral

instance Pushable Float where
  push :: Float -> Lua ()
push = Float -> Lua ()
forall a. RealFloat a => a -> Lua ()
pushRealFloat

instance Pushable Double where
  push :: Double -> Lua ()
push = Double -> Lua ()
forall a. RealFloat a => a -> Lua ()
pushRealFloat

instance {-# OVERLAPS #-} Pushable [Char] where
  push :: [Char] -> Lua ()
push = [Char] -> Lua ()
pushString

instance Pushable a => Pushable [a] where
  push :: [a] -> Lua ()
push = Pusher a -> [a] -> Lua ()
forall a. Pusher a -> [a] -> Lua ()
Push.pushList Pusher a
forall a. Pushable a => a -> Lua ()
push

instance (Pushable a, Pushable b) => Pushable (Map a b) where
  push :: Map a b -> Lua ()
push = Pusher a -> Pusher b -> Map a b -> Lua ()
forall a b. Pusher a -> Pusher b -> Pusher (Map a b)
pushMap Pusher a
forall a. Pushable a => a -> Lua ()
push Pusher b
forall a. Pushable a => a -> Lua ()
push

instance Pushable a => Pushable (Set a) where
  push :: Set a -> Lua ()
push = Pusher a -> Set a -> Lua ()
forall a. Pusher a -> Pusher (Set a)
pushSet Pusher a
forall a. Pushable a => a -> Lua ()
push

-- | Push list as numerically indexed table.
pushList :: Pushable a => [a] -> Lua ()
pushList :: [a] -> Lua ()
pushList = Pusher a -> [a] -> Lua ()
forall a. Pusher a -> [a] -> Lua ()
Push.pushList Pusher a
forall a. Pushable a => a -> Lua ()
push

--
-- Tuples
--
instance (Pushable a, Pushable b) => Pushable (a, b) where
  push :: (a, b) -> Lua ()
push (a
a, b
b) = do
    Lua ()
newtable
    Integer -> a -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
1 a
a
    Integer -> b -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
2 b
b

instance (Pushable a, Pushable b, Pushable c) =>
         Pushable (a, b, c)
 where
  push :: (a, b, c) -> Lua ()
push (a
a, b
b, c
c) = do
    Lua ()
newtable
    Integer -> a -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
1 a
a
    Integer -> b -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
2 b
b
    Integer -> c -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
3 c
c

instance (Pushable a, Pushable b, Pushable c, Pushable d) =>
         Pushable (a, b, c, d)
 where
  push :: (a, b, c, d) -> Lua ()
push (a
a, b
b, c
c, d
d) = do
    Lua ()
newtable
    Integer -> a -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
1 a
a
    Integer -> b -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
2 b
b
    Integer -> c -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
3 c
c
    Integer -> d -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
4 d
d

instance (Pushable a, Pushable b, Pushable c,
          Pushable d, Pushable e) =>
         Pushable (a, b, c, d, e)
 where
  push :: (a, b, c, d, e) -> Lua ()
push (a
a, b
b, c
c, d
d, e
e) = do
    Lua ()
newtable
    Integer -> a -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
1 a
a
    Integer -> b -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
2 b
b
    Integer -> c -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
3 c
c
    Integer -> d -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
4 d
d
    Integer -> e -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
5 e
e

instance (Pushable a, Pushable b, Pushable c,
          Pushable d, Pushable e, Pushable f) =>
         Pushable (a, b, c, d, e, f)
 where
  push :: (a, b, c, d, e, f) -> Lua ()
push (a
a, b
b, c
c, d
d, e
e, f
f) = do
    Lua ()
newtable
    Integer -> a -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
1 a
a
    Integer -> b -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
2 b
b
    Integer -> c -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
3 c
c
    Integer -> d -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
4 d
d
    Integer -> e -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
5 e
e
    Integer -> f -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
6 f
f

instance (Pushable a, Pushable b, Pushable c, Pushable d,
          Pushable e, Pushable f, Pushable g) =>
         Pushable (a, b, c, d, e, f, g)
 where
  push :: (a, b, c, d, e, f, g) -> Lua ()
push (a
a, b
b, c
c, d
d, e
e, f
f, g
g) = do
    Lua ()
newtable
    Integer -> a -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
1 a
a
    Integer -> b -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
2 b
b
    Integer -> c -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
3 c
c
    Integer -> d -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
4 d
d
    Integer -> e -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
5 e
e
    Integer -> f -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
6 f
f
    Integer -> g -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
7 g
g

instance (Pushable a, Pushable b, Pushable c, Pushable d,
          Pushable e, Pushable f, Pushable g, Pushable h) =>
         Pushable (a, b, c, d, e, f, g, h)
 where
  push :: (a, b, c, d, e, f, g, h) -> Lua ()
push (a
a, b
b, c
c, d
d, e
e, f
f, g
g, h
h) = do
    Lua ()
newtable
    Integer -> a -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
1 a
a
    Integer -> b -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
2 b
b
    Integer -> c -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
3 c
c
    Integer -> d -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
4 d
d
    Integer -> e -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
5 e
e
    Integer -> f -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
6 f
f
    Integer -> g -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
7 g
g
    Integer -> h -> Lua ()
forall a. Pushable a => Integer -> a -> Lua ()
addRawInt Integer
8 h
h

-- | Set numeric key/value in table at the top of the stack.
addRawInt :: Pushable a => Lua.Integer -> a -> Lua ()
addRawInt :: Integer -> a -> Lua ()
addRawInt Integer
idx a
val = do
  a -> Lua ()
forall a. Pushable a => a -> Lua ()
push a
val
  StackIndex -> Integer -> Lua ()
rawseti (-StackIndex
2) Integer
idx