{-
Bustle.GVariant: bindings for GVariant
Copyright © 2020 Will Thompson

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-}
{-# LANGUAGE ForeignFunctionInterface #-}
module Bustle.GVariant
  (
-- * Types
    GVariant
  , TypeAnnotate(..)

-- * Constructors
  , makeNewGVariant
  , wrapNewGVariant

-- * Methods
  , variantGetChild
  , variantGetString
  , variantPrint

  )
where

import Foreign.ForeignPtr
import Foreign.Ptr
import Foreign.C

import System.Glib.UTFString

import Control.Monad (guard)
import Control.Monad.Trans (liftIO)
import Control.Monad.Trans.Maybe

data TypeAnnotate = NoAnnotations
                  | WithAnnotations
  deriving
    (Show, Ord, Eq, Enum)

newtype GVariant = GVariant { unGVariant :: ForeignPtr GVariant }
    deriving (Eq, Ord, Show)

makeNewGVariant :: IO (Ptr GVariant)
                -> IO GVariant
makeNewGVariant act = wrapNewGVariant (act >>= g_variant_ref)

wrapNewGVariant :: IO (Ptr GVariant)
                -> IO GVariant
wrapNewGVariant act = do
    vPtr <- act
    v <- newForeignPtr g_variant_unref vPtr
    return $ GVariant v

-- Foreign imports
foreign import ccall unsafe "g_variant_is_of_type"
    g_variant_is_of_type :: Ptr a
                         -> CString
                         -> IO CInt

foreign import ccall unsafe "g_variant_n_children"
    g_variant_n_children :: Ptr a
                             -> IO CSize

foreign import ccall unsafe "g_variant_get_child_value"
    g_variant_get_child_value :: Ptr a
                              -> CSize
                              -> IO (Ptr a)

foreign import ccall unsafe "g_variant_get_string"
    g_variant_get_string :: Ptr a
                         -> Ptr CSize
                         -> IO CString

foreign import ccall unsafe "g_variant_print"
    g_variant_print :: Ptr a
                    -> CInt
                    -> IO CString

foreign import ccall unsafe "g_variant_ref"
    g_variant_ref :: Ptr GVariant
                  -> IO (Ptr GVariant)

foreign import ccall unsafe "&g_variant_unref"
    g_variant_unref :: FunPtr (Ptr GVariant -> IO ())

-- Bindings
variantNChildren :: GVariant
                    -> IO Word
variantNChildren v = withForeignPtr (unGVariant v) $ \vPtr -> do
    fromIntegral <$> g_variant_n_children vPtr

variantGetChild :: GVariant
                -> Word
                -> IO (Maybe GVariant)
variantGetChild v i = withForeignPtr (unGVariant v) $ \vPtr -> runMaybeT $ do
    n <- liftIO $ variantNChildren v
    guard (i < n)
    liftIO $ wrapNewGVariant $ g_variant_get_child_value vPtr (fromIntegral i)

variantGetString :: GVariant
                 -> IO (Maybe String)
variantGetString v = withForeignPtr (unGVariant v) $ \vPtr -> runMaybeT $ do
    r <- liftIO $ withCString "s" $ g_variant_is_of_type vPtr
    guard (r /= 0)
    s <- liftIO $ g_variant_get_string vPtr nullPtr
    liftIO $ peekUTFString s

variantPrint :: GVariant
             -> TypeAnnotate
             -> IO String
variantPrint v annotate = withForeignPtr (unGVariant v) $ \vPtr -> do
    cstr <- g_variant_print vPtr (fromIntegral $ fromEnum annotate)
    readUTFString cstr
