    {- |
       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 invokation returns a tripple consisting of an item's
    -- filename, MIME type, and content.
    pushIterator :: MediaBag -> Lua NumResults
    pushIterator :: MediaBag -> Lua NumResults
    pushIterator mb :: 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 3

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

    -- | Push a @MediaItems@ element to the stack.
    pushMediaItems :: MediaItems -> Lua ()
    pushMediaItems :: MediaItems -> Lua ()
    pushMediaItems xs :: 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 :: Ptr (StablePtr MediaItems)
    ptr _ = do
      (MediaItems items :: [(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
        [] -> 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)
        (key :: String
    key, mt :: MimeType
    mt, content :: ByteString
    content):xs :: [(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 3