{- |
   Module      : Text.Pandoc.Lua.Marshaling.MediaBag
   Copyright   : © 2012-2020 John MacFarlane
                 © 2017-2020 Albert Krewinkel
   License     : GNU GPL, version 2 or above
   Maintainer  : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
   Stability   : alpha

Instances to marshal (push) and unmarshal (peek) media data.
-}
module Text.Pandoc.Lua.Marshaling.MediaBag (pushIterator) where

import Foreign.Ptr (Ptr)
import Foreign.StablePtr (StablePtr, deRefStablePtr, newStablePtr)
import Foreign.Lua (Lua, NumResults, Peekable, Pushable, StackIndex)
import Foreign.Lua.Types.Peekable (reportValueOnFailure)
import Foreign.Lua.Userdata (ensureUserdataMetatable, pushAnyWithMetatable,
                             toAnyWithName)
import Text.Pandoc.MediaBag (MediaBag, mediaItems)
import Text.Pandoc.MIME (MimeType)
import Text.Pandoc.Lua.Marshaling.AnyValue (AnyValue (..))

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

-- | A list of 'MediaBag' items.
newtype MediaItems = MediaItems [(String, MimeType, BL.ByteString)]

instance Pushable MediaItems where
  push :: MediaItems -> Lua ()
push = MediaItems -> Lua ()
pushMediaItems

instance Peekable MediaItems where
  peek :: StackIndex -> Lua MediaItems
peek = StackIndex -> Lua MediaItems
peekMediaItems

-- | Push an iterator triple to be used with Lua's @for@ loop construct.
-- Each iterator invocation returns a triple containing the item's
-- filename, MIME type, and content.
pushIterator :: MediaBag -> Lua NumResults
pushIterator :: MediaBag -> Lua NumResults
pushIterator MediaBag
mb = do
  (Ptr (StablePtr MediaItems) -> AnyValue -> Lua NumResults)
-> Lua ()
forall a. ToHaskellFunction a => a -> Lua ()
Lua.pushHaskellFunction Ptr (StablePtr MediaItems) -> AnyValue -> Lua NumResults
nextItem
  MediaItems -> Lua ()
forall a. Pushable a => a -> Lua ()
Lua.push ([(String, MimeType, ByteString)] -> MediaItems
MediaItems ([(String, MimeType, ByteString)] -> MediaItems)
-> [(String, MimeType, ByteString)] -> MediaItems
forall a b. (a -> b) -> a -> b
$ MediaBag -> [(String, MimeType, ByteString)]
mediaItems MediaBag
mb)
  Lua ()
Lua.pushnil
  NumResults -> Lua NumResults
forall (m :: * -> *) a. Monad m => a -> m a
return NumResults
3

-- | Lua type name for @'MediaItems'@.
mediaItemsTypeName :: String
mediaItemsTypeName :: String
mediaItemsTypeName = String
"pandoc MediaItems"

-- | Push a @MediaItems@ element to the stack.
pushMediaItems :: MediaItems -> Lua ()
pushMediaItems :: MediaItems -> Lua ()
pushMediaItems MediaItems
xs = Lua () -> MediaItems -> Lua ()
forall a. Lua () -> a -> Lua ()
pushAnyWithMetatable Lua ()
pushMT MediaItems
xs
 where
  pushMT :: Lua ()
pushMT = String -> Lua () -> Lua ()
ensureUserdataMetatable String
mediaItemsTypeName (() -> Lua ()
forall (m :: * -> *) a. Monad m => a -> m a
return ())

-- | Retrieve a @MediaItems@ element from the stack.
peekMediaItems :: StackIndex -> Lua MediaItems
peekMediaItems :: StackIndex -> Lua MediaItems
peekMediaItems = String
-> (StackIndex -> Lua (Maybe MediaItems))
-> StackIndex
-> Lua MediaItems
forall a.
String -> (StackIndex -> Lua (Maybe a)) -> StackIndex -> Lua a
reportValueOnFailure String
mediaItemsTypeName
                 (StackIndex -> String -> Lua (Maybe MediaItems)
forall a. StackIndex -> String -> Lua (Maybe a)
`toAnyWithName` String
mediaItemsTypeName)

-- | Retrieve a list of items from an iterator state, return the first
-- item (if present), and advance the state.
nextItem :: Ptr (StablePtr MediaItems) -> AnyValue -> Lua NumResults
nextItem :: Ptr (StablePtr MediaItems) -> AnyValue -> Lua NumResults
nextItem Ptr (StablePtr MediaItems)
ptr AnyValue
_ = do
  (MediaItems [(String, MimeType, ByteString)]
items) <- IO MediaItems -> Lua MediaItems
forall (m :: * -> *) a. MonadIO m => IO a -> m a
Lua.liftIO (IO MediaItems -> Lua MediaItems)
-> IO MediaItems -> Lua MediaItems
forall a b. (a -> b) -> a -> b
$ StablePtr MediaItems -> IO MediaItems
forall a. StablePtr a -> IO a
deRefStablePtr (StablePtr MediaItems -> IO MediaItems)
-> IO (StablePtr MediaItems) -> IO MediaItems
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Ptr (StablePtr MediaItems) -> IO (StablePtr MediaItems)
forall a. Storable a => Ptr a -> IO a
Storable.peek Ptr (StablePtr MediaItems)
ptr
  case [(String, MimeType, ByteString)]
items of
    [] -> NumResults
2 NumResults -> Lua () -> Lua NumResults
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ (Lua ()
Lua.pushnil Lua () -> Lua () -> Lua ()
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Lua ()
Lua.pushnil)
    (String
key, MimeType
mt, ByteString
content):[(String, MimeType, ByteString)]
xs -> do
      IO () -> Lua ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
Lua.liftIO (IO () -> Lua ()) -> IO () -> Lua ()
forall a b. (a -> b) -> a -> b
$ Ptr (StablePtr MediaItems) -> StablePtr MediaItems -> IO ()
forall a. Storable a => Ptr a -> a -> IO ()
Storable.poke Ptr (StablePtr MediaItems)
ptr (StablePtr MediaItems -> IO ())
-> IO (StablePtr MediaItems) -> IO ()
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< MediaItems -> IO (StablePtr MediaItems)
forall a. a -> IO (StablePtr a)
newStablePtr ([(String, MimeType, ByteString)] -> MediaItems
MediaItems [(String, MimeType, ByteString)]
xs)
      String -> Lua ()
forall a. Pushable a => a -> Lua ()
Lua.push String
key
      MimeType -> Lua ()
forall a. Pushable a => a -> Lua ()
Lua.push MimeType
mt
      ByteString -> Lua ()
forall a. Pushable a => a -> Lua ()
Lua.push ByteString
content
      NumResults -> Lua NumResults
forall (m :: * -> *) a. Monad m => a -> m a
return NumResults
3