{-# LINE 1 "Text/XkbCommon/Context.hsc" #-}
{-# LANGUAGE CPP, ForeignFunctionInterface #-}
{-# LINE 2 "Text/XkbCommon/Context.hsc" #-}

module Text.XkbCommon.Context
   ( Context(..),

     ContextFlags, defaultFlags, pureFlags, contextNoDefaultIncludes, contextNoEnvironment,

     newContext, getIncludePaths, setIncludePaths,
     appendIncludePath, numIncludePaths, clearIncludePath, appendDefaultIncludePath,
     includePathShow,
   ) where

import Foreign
import Foreign.C
import Control.Monad (liftM)
import Data.Maybe
import System.FilePath

import Text.XkbCommon.InternalTypes


{-# LINE 22 "Text/XkbCommon/Context.hsc" #-}



-- | Construct a new Xkb context from creation preferences.
--   xkb_context_new can fail if the default include path does not exist.
--
--   (@xkb_context_new@)
newContext :: ContextFlags -> IO (Maybe Context)
newContext c = do
   k <- c_new_context c
   if k == nullPtr
      then return Nothing
      else do
         l <- newForeignPtr c_unref_context k
         return $ Just $ toContext l

-- | Get the current include paths of a 'Context'.
--   Upon 'Keymap' creation, these directories will be searched for keymap definitions.
getIncludePaths :: Context -> IO [FilePath]
getIncludePaths ctx = do
   numPaths <- numIncludePaths ctx
   sequence [includePathShow ctx i | i<-[1..numPaths]]

-- | Set a new list of include paths for a 'Context'.
setIncludePaths :: Context -- ^ Context whose search paths we are changing
                -> [FilePath] -- ^ New list of search paths
                -> Bool -- ^ Set to True if you also want to search on the default path
                -> IO (Maybe ()) -- ^ returns Just () if addition of at least one path succeeded
setIncludePaths ctx list appendDefault = do
   clearIncludePath ctx
   let listMaybeWith = if appendDefault
                          then (appendDefaultIncludePath ctx:map addPath list)
                          else map addPath list
   success <- fmap or $ fmap (fmap isJust) $ sequence listMaybeWith
   return $ if success
               then Just ()
               else Nothing
       where
          addPath path = appendIncludePath ctx path

-- | Remove all 'Keymap' file search paths from a 'Context'.
--
--   Preferred API is to use 'getIncludePaths' and 'setIncludePaths'
--
--   (@xkb_context_include_path_clear@)
clearIncludePath :: Context -> IO ()
clearIncludePath ctx = withContext ctx $ \ ptr -> c_clear_includes ptr

-- stateful handling of Xkb context search paths for keymaps
-- fails if the path does not exist
-- | Append a search path for 'Keymap' files to a 'Context'. (@xkb_context_include_path_append@)
--
--   Preferred API is to use 'getIncludePaths' and 'setIncludePaths'
--
appendIncludePath :: Context -> FilePath -> IO (Maybe ())
appendIncludePath c str = withCString str $
   \ cstr -> withContext c $
      \ ptr -> do
         err <- c_append_include_path_context ptr cstr
         return $ if err == 1
            then Just ()
            else Nothing

-- | Append the default 'Keymap' search path (whose location depends on libxkbcommon compile-time settings) (@xkb_context_include_path_append_default@)
--
--   Preferred API is to use 'getIncludePaths' and 'setIncludePaths'
--
appendDefaultIncludePath :: Context -> IO (Maybe ())
appendDefaultIncludePath ctx = withContext ctx $ \ ptr -> do
   ret <- c_append_default_include ptr -- returns 0 on error
   return (if ret == 0 then Nothing else Just ())

-- | (@xkb_context_num_include_paths@)
--
--   Preferred API is to use 'getIncludePaths' and 'setIncludePaths'
--
numIncludePaths :: Context -> IO Int
numIncludePaths c = withContext c $ liftM fromIntegral . c_num_include_paths_context

-- c_show_include_path :: Ptr CContext -> CUInt -> IO CString
-- | Get a specific include path from the context's include path. (@xkb_context_include_path_get@)
--
--   Preferred API is to use 'getIncludePaths' and 'setIncludePaths'
--
includePathShow :: Context -> Int -> IO FilePath
includePathShow ctx idx = withContext ctx $ \ ptr -> c_show_include_path ptr (fromIntegral idx) >>= peekCString


-- FOREIGN CCALLS


-- context related

foreign import ccall unsafe "xkbcommon/xkbcommon.h xkb_context_new"
   c_new_context :: ContextFlags -> IO (Ptr CContext)

foreign import ccall unsafe "xkbcommon/xkbcommon.h &xkb_context_unref"
   c_unref_context :: FinalizerPtr CContext

foreign import ccall unsafe "xkbcommon/xkbcommon.h xkb_context_include_path_append"
   c_append_include_path_context :: Ptr CContext -> CString -> IO CInt

foreign import ccall unsafe "xkbcommon/xkbcommon.h xkb_context_num_include_paths"
   c_num_include_paths_context :: Ptr CContext -> IO CUInt

-- int    xkb_context::xkb_context_include_path_append_default (struct xkb_context *context)
--     Append the default include paths to the contexts include path.
foreign import ccall unsafe "xkbcommon/xkbcommon.h xkb_context_include_path_append_default"
   c_append_default_include :: Ptr CContext -> IO CInt

-- int    xkb_context::xkb_context_include_path_reset_defaults (struct xkb_context *context)
--     Reset the context's include path to the default.
--foreign import ccall unsafe "xkbcommon/xkbcommon.h xkb_context_include_path_reset_defaults"

-- void    xkb_context::xkb_context_include_path_clear (struct xkb_context *context)
--     Remove all entries from the context's include path.
foreign import ccall unsafe "xkbcommon/xkbcommon.h xkb_context_include_path_clear"
   c_clear_includes :: Ptr CContext -> IO ()

-- const char *    xkb_context::xkb_context_include_path_get (struct xkb_context *context, unsigned int index)
--     Get a specific include path from the context's include path.
foreign import ccall unsafe "xkbcommon/xkbcommon.h xkb_context_include_path_get"
   c_show_include_path :: Ptr CContext -> CUInt -> IO CString


-- The foreign calls below are not yet bound... not sure I want to at this stage.

-- logging related

-- void    xkb_context::xkb_context_set_log_level (struct xkb_context *context, enum xkb_log_level level)
--     Set the current logging level.
foreign import ccall unsafe "xkbcommon/xkbcommon.h xkb_context_set_log_level"
   c_set_log_level :: Ptr CContext -> CLogLevel -> IO ()

-- enum xkb_log_level    xkb_context::xkb_context_get_log_level (struct xkb_context *context)
--     Get the current logging level.
foreign import ccall unsafe "xkbcommon/xkbcommon.h xkb_context_get_log_level"
   c_get_log_level :: Ptr CContext -> IO CLogLevel

-- void    xkb_context::xkb_context_set_log_verbosity (struct xkb_context *context, int verbosity)
--     Sets the current logging verbosity.
foreign import ccall unsafe "xkbcommon/xkbcommon.h xkb_context_set_log_verbosity"
   c_set_log_verbosity :: Ptr CContext -> CInt -> IO ()

-- int    xkb_context::xkb_context_get_log_verbosity (struct xkb_context *context)
--     Get the current logging verbosity of the context.
foreign import ccall unsafe "xkbcommon/xkbcommon.h xkb_context_get_log_verbosity"
   c_get_log_verbosity :: Ptr CContext -> IO CInt

-- we have to manually translate this in C because the haskell FFI does not support va_list!
-- void    xkb_context::xkb_context_set_log_fn (struct xkb_context *context, void(*log_fn)(struct xkb_context *context, enum xkb_log_level level, const char *format, va_list args))
--     Set a custom function to handle logging messages.
-- foreign import ccall unsafe "xkbcommon/xkbcommon.h xkb_context_set_log_fn"
--    c_set_log_fun :: Ptr CContext -> FunPtr (Ptr CContext -> CLogLevel -> CString -> #{type va_list} -> IO ()) -> IO ()