{- |
Copyright  : Will Thompson, Iñaki García Etxebarria and Jonas Platte
License    : LGPL-2.1
Maintainer : Iñaki García Etxebarria (garetxe@gmail.com)

'GI.Gio.Interfaces.Initable.Initable' is implemented by objects that can fail during
initialization. If an object implements this interface then
it must be initialized as the first thing after construction,
either via 'GI.Gio.Interfaces.Initable.initableInit' or 'GI.Gio.Interfaces.AsyncInitable.asyncInitableInitAsync'
(the latter is only available if it also implements 'GI.Gio.Interfaces.AsyncInitable.AsyncInitable').

If the object is not initialized, or initialization returns with an
error, then all operations on the object except 'GI.GObject.Objects.Object.objectRef' and
'GI.GObject.Objects.Object.objectUnref' are considered to be invalid, and have undefined
behaviour. They will often fail with @/g_critical()/@ or @/g_warning()/@, but
this must not be relied on.

Users of objects implementing this are not intended to use
the interface method directly, instead it will be used automatically
in various ways. For C applications you generally just call
@/g_initable_new()/@ directly, or indirectly via a @/foo_thing_new()/@ wrapper.
This will call 'GI.Gio.Interfaces.Initable.initableInit' under the cover, returning 'Nothing' and
setting a 'GError' on failure (at which point the instance is
unreferenced).

For bindings in languages where the native constructor supports
exceptions the binding could check for objects implemention @/GInitable/@
during normal construction and automatically initialize them, throwing
an exception on failure.
-}

module GI.Gio.Interfaces.Initable
    ( 

-- * Exported types
    Initable(..)                            ,
    noInitable                              ,
    IsInitable                              ,
    toInitable                              ,


 -- * Methods
-- ** init #method:init#
    InitableInitMethodInfo                  ,
    initableInit                            ,


-- ** newv #method:newv#
    initableNewv                            ,




    ) where

import Data.GI.Base.ShortPrelude
import qualified Data.GI.Base.ShortPrelude as SP
import qualified Data.GI.Base.Overloading as O
import qualified Prelude as P

import qualified Data.GI.Base.Attributes as GI.Attributes
import qualified Data.GI.Base.ManagedPtr as B.ManagedPtr
import qualified Data.GI.Base.GError as B.GError
import qualified Data.GI.Base.GVariant as B.GVariant
import qualified Data.GI.Base.GParamSpec as B.GParamSpec
import qualified Data.GI.Base.CallStack as B.CallStack
import qualified Data.Text as T
import qualified Data.ByteString.Char8 as B
import qualified Data.Map as Map
import qualified Foreign.Ptr as FP

import qualified GI.GObject.Objects.Object as GObject.Object
import qualified GI.GObject.Structs.Parameter as GObject.Parameter
import {-# SOURCE #-} qualified GI.Gio.Objects.Cancellable as Gio.Cancellable

-- interface Initable 
newtype Initable = Initable (ManagedPtr Initable)
noInitable :: Maybe Initable
noInitable = Nothing

type family ResolveInitableMethod (t :: Symbol) (o :: *) :: * where
    ResolveInitableMethod "bindProperty" o = GObject.Object.ObjectBindPropertyMethodInfo
    ResolveInitableMethod "bindPropertyFull" o = GObject.Object.ObjectBindPropertyFullMethodInfo
    ResolveInitableMethod "forceFloating" o = GObject.Object.ObjectForceFloatingMethodInfo
    ResolveInitableMethod "freezeNotify" o = GObject.Object.ObjectFreezeNotifyMethodInfo
    ResolveInitableMethod "init" o = InitableInitMethodInfo
    ResolveInitableMethod "isFloating" o = GObject.Object.ObjectIsFloatingMethodInfo
    ResolveInitableMethod "notify" o = GObject.Object.ObjectNotifyMethodInfo
    ResolveInitableMethod "notifyByPspec" o = GObject.Object.ObjectNotifyByPspecMethodInfo
    ResolveInitableMethod "ref" o = GObject.Object.ObjectRefMethodInfo
    ResolveInitableMethod "refSink" o = GObject.Object.ObjectRefSinkMethodInfo
    ResolveInitableMethod "replaceData" o = GObject.Object.ObjectReplaceDataMethodInfo
    ResolveInitableMethod "replaceQdata" o = GObject.Object.ObjectReplaceQdataMethodInfo
    ResolveInitableMethod "runDispose" o = GObject.Object.ObjectRunDisposeMethodInfo
    ResolveInitableMethod "stealData" o = GObject.Object.ObjectStealDataMethodInfo
    ResolveInitableMethod "stealQdata" o = GObject.Object.ObjectStealQdataMethodInfo
    ResolveInitableMethod "thawNotify" o = GObject.Object.ObjectThawNotifyMethodInfo
    ResolveInitableMethod "unref" o = GObject.Object.ObjectUnrefMethodInfo
    ResolveInitableMethod "watchClosure" o = GObject.Object.ObjectWatchClosureMethodInfo
    ResolveInitableMethod "getData" o = GObject.Object.ObjectGetDataMethodInfo
    ResolveInitableMethod "getProperty" o = GObject.Object.ObjectGetPropertyMethodInfo
    ResolveInitableMethod "getQdata" o = GObject.Object.ObjectGetQdataMethodInfo
    ResolveInitableMethod "setData" o = GObject.Object.ObjectSetDataMethodInfo
    ResolveInitableMethod "setProperty" o = GObject.Object.ObjectSetPropertyMethodInfo
    ResolveInitableMethod l o = O.MethodResolutionFailed l o

instance (info ~ ResolveInitableMethod t Initable, O.MethodInfo info Initable p) => O.IsLabelProxy t (Initable -> p) where
    fromLabelProxy _ = O.overloadedMethod (O.MethodProxy :: O.MethodProxy info)

#if MIN_VERSION_base(4,9,0)
instance (info ~ ResolveInitableMethod t Initable, O.MethodInfo info Initable p) => O.IsLabel t (Initable -> p) where
    fromLabel _ = O.overloadedMethod (O.MethodProxy :: O.MethodProxy info)
#endif

instance O.HasAttributeList Initable
type instance O.AttributeList Initable = InitableAttributeList
type InitableAttributeList = ('[ ] :: [(Symbol, *)])

type instance O.SignalList Initable = InitableSignalList
type InitableSignalList = ('[ '("notify", GObject.Object.ObjectNotifySignalInfo)] :: [(Symbol, *)])

foreign import ccall "g_initable_get_type"
    c_g_initable_get_type :: IO GType

instance GObject Initable where
    gobjectType _ = c_g_initable_get_type
    

class GObject o => IsInitable o
#if MIN_VERSION_base(4,9,0)
instance {-# OVERLAPPABLE #-} (GObject a, O.UnknownAncestorError Initable a) =>
    IsInitable a
#endif
instance IsInitable Initable
instance GObject.Object.IsObject Initable

toInitable :: IsInitable o => o -> IO Initable
toInitable = unsafeCastTo Initable

-- method Initable::init
-- method type : OrdinaryMethod
-- Args : [Arg {argCName = "initable", argType = TInterface (Name {namespace = "Gio", name = "Initable"}), direction = DirectionIn, mayBeNull = False, argDoc = Documentation {rawDocText = Just "a #GInitable.", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferNothing},Arg {argCName = "cancellable", argType = TInterface (Name {namespace = "Gio", name = "Cancellable"}), direction = DirectionIn, mayBeNull = True, argDoc = Documentation {rawDocText = Just "optional #GCancellable object, %NULL to ignore.", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferNothing}]
-- Lengths : []
-- returnType : Just (TBasicType TBoolean)
-- throws : True
-- Skip return : False

foreign import ccall "g_initable_init" g_initable_init :: 
    Ptr Initable ->                         -- initable : TInterface (Name {namespace = "Gio", name = "Initable"})
    Ptr Gio.Cancellable.Cancellable ->      -- cancellable : TInterface (Name {namespace = "Gio", name = "Cancellable"})
    Ptr (Ptr GError) ->                     -- error
    IO CInt

{- |
Initializes the object implementing the interface.

The object must be initialized before any real use after initial
construction, either with this function or 'GI.Gio.Interfaces.AsyncInitable.asyncInitableInitAsync'.

Implementations may also support cancellation. If /@cancellable@/ is not 'Nothing',
then initialization can be cancelled by triggering the cancellable object
from another thread. If the operation was cancelled, the error
'GI.Gio.Enums.IOErrorEnumCancelled' will be returned. If /@cancellable@/ is not 'Nothing' and
the object doesn\'t support cancellable initialization the error
'GI.Gio.Enums.IOErrorEnumNotSupported' will be returned.

If the object is not initialized, or initialization returns with an
error, then all operations on the object except 'GI.GObject.Objects.Object.objectRef' and
'GI.GObject.Objects.Object.objectUnref' are considered to be invalid, and have undefined
behaviour. See the [introduction][ginitable] for more details.

Implementations of this method must be idempotent, i.e. multiple calls
to this function with the same argument should return the same results.
Only the first call initializes the object, further calls return the result
of the first call. This is so that it\'s safe to implement the singleton
pattern in the GObject constructor function.

@since 2.22
-}
initableInit ::
    (B.CallStack.HasCallStack, MonadIO m, IsInitable a, Gio.Cancellable.IsCancellable b) =>
    a
    {- ^ /@initable@/: a 'GI.Gio.Interfaces.Initable.Initable'. -}
    -> Maybe (b)
    {- ^ /@cancellable@/: optional 'GI.Gio.Objects.Cancellable.Cancellable' object, 'Nothing' to ignore. -}
    -> m ()
    {- ^ /(Can throw 'Data.GI.Base.GError.GError')/ -}
initableInit initable cancellable = liftIO $ do
    initable' <- unsafeManagedPtrCastPtr initable
    maybeCancellable <- case cancellable of
        Nothing -> return nullPtr
        Just jCancellable -> do
            jCancellable' <- unsafeManagedPtrCastPtr jCancellable
            return jCancellable'
    onException (do
        _ <- propagateGError $ g_initable_init initable' maybeCancellable
        touchManagedPtr initable
        whenJust cancellable touchManagedPtr
        return ()
     ) (do
        return ()
     )

data InitableInitMethodInfo
instance (signature ~ (Maybe (b) -> m ()), MonadIO m, IsInitable a, Gio.Cancellable.IsCancellable b) => O.MethodInfo InitableInitMethodInfo a signature where
    overloadedMethod _ = initableInit

-- method Initable::newv
-- method type : MemberFunction
-- Args : [Arg {argCName = "object_type", argType = TBasicType TGType, direction = DirectionIn, mayBeNull = False, argDoc = Documentation {rawDocText = Just "a #GType supporting #GInitable.", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferNothing},Arg {argCName = "n_parameters", argType = TBasicType TUInt, direction = DirectionIn, mayBeNull = False, argDoc = Documentation {rawDocText = Just "the number of parameters in @parameters", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferNothing},Arg {argCName = "parameters", argType = TCArray False (-1) 1 (TInterface (Name {namespace = "GObject", name = "Parameter"})), direction = DirectionIn, mayBeNull = False, argDoc = Documentation {rawDocText = Just "the parameters to use to construct the object", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferNothing},Arg {argCName = "cancellable", argType = TInterface (Name {namespace = "Gio", name = "Cancellable"}), direction = DirectionIn, mayBeNull = True, argDoc = Documentation {rawDocText = Just "optional #GCancellable object, %NULL to ignore.", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferNothing}]
-- Lengths : [Arg {argCName = "n_parameters", argType = TBasicType TUInt, direction = DirectionIn, mayBeNull = False, argDoc = Documentation {rawDocText = Just "the number of parameters in @parameters", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferNothing}]
-- returnType : Just (TInterface (Name {namespace = "GObject", name = "Object"}))
-- throws : True
-- Skip return : False

foreign import ccall "g_initable_newv" g_initable_newv :: 
    CGType ->                               -- object_type : TBasicType TGType
    Word32 ->                               -- n_parameters : TBasicType TUInt
    Ptr GObject.Parameter.Parameter ->      -- parameters : TCArray False (-1) 1 (TInterface (Name {namespace = "GObject", name = "Parameter"}))
    Ptr Gio.Cancellable.Cancellable ->      -- cancellable : TInterface (Name {namespace = "Gio", name = "Cancellable"})
    Ptr (Ptr GError) ->                     -- error
    IO (Ptr GObject.Object.Object)

{- |
Helper function for constructing 'GI.Gio.Interfaces.Initable.Initable' object. This is
similar to 'GI.GObject.Objects.Object.objectNew' but also initializes the object
and returns 'Nothing', setting an error on failure.

@since 2.22
-}
initableNewv ::
    (B.CallStack.HasCallStack, MonadIO m, Gio.Cancellable.IsCancellable a) =>
    GType
    {- ^ /@objectType@/: a 'GType' supporting 'GI.Gio.Interfaces.Initable.Initable'. -}
    -> [GObject.Parameter.Parameter]
    {- ^ /@parameters@/: the parameters to use to construct the object -}
    -> Maybe (a)
    {- ^ /@cancellable@/: optional 'GI.Gio.Objects.Cancellable.Cancellable' object, 'Nothing' to ignore. -}
    -> m GObject.Object.Object
    {- ^ __Returns:__ a newly allocated
     'GI.GObject.Objects.Object.Object', or 'Nothing' on error /(Can throw 'Data.GI.Base.GError.GError')/ -}
initableNewv objectType parameters cancellable = liftIO $ do
    let nParameters = fromIntegral $ length parameters
    let objectType' = gtypeToCGType objectType
    parameters' <- mapM unsafeManagedPtrGetPtr parameters
    parameters'' <- packBlockArray 32 parameters'
    maybeCancellable <- case cancellable of
        Nothing -> return nullPtr
        Just jCancellable -> do
            jCancellable' <- unsafeManagedPtrCastPtr jCancellable
            return jCancellable'
    onException (do
        result <- propagateGError $ g_initable_newv objectType' nParameters parameters'' maybeCancellable
        checkUnexpectedReturnNULL "initableNewv" result
        result' <- (wrapObject GObject.Object.Object) result
        mapM_ touchManagedPtr parameters
        whenJust cancellable touchManagedPtr
        freeMem parameters''
        return result'
     ) (do
        freeMem parameters''
     )