    {- |
    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.Converter.Converter' is implemented by objects that convert
    binary data in various ways. The conversion can be
    stateful and may fail at any place.

    Some example conversions are: character set conversion,
    compression, decompression and regular expression
    replace.

    /Since: 2.24/
    -}

    #define ENABLE_OVERLOADING \
           (!defined(__HADDOCK_VERSION__))

    module GI.Gio.Interfaces.Converter
        (

    -- * Exported types
        Converter(..)                           ,
        noConverter                             ,
        IsConverter                             ,
        toConverter                             ,

     -- * Methods
    -- ** convert #method:convert#

    #if ENABLE_OVERLOADING
        ConverterConvertMethodInfo              ,
    #endif
        converterConvert                        ,

    -- ** reset #method:reset#

    #if ENABLE_OVERLOADING
        ConverterResetMethodInfo                ,
    #endif
        converterReset                          ,

        ) 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.GValue as B.GValue
    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 {-# SOURCE #-} qualified GI.Gio.Enums as Gio.Enums
    import {-# SOURCE #-} qualified GI.Gio.Flags as Gio.Flags

    -- interface Converter
    -- | Memory-managed wrapper type.
    newtype Converter = Converter (ManagedPtr Converter)
    -- | A convenience alias for `Nothing` :: `Maybe` `Converter`.
    noConverter :: Maybe Converter
    noConverter = Nothing

    #if ENABLE_OVERLOADING
    type instance O.SignalList Converter = ConverterSignalList
    type ConverterSignalList = ('[ '("notify", GObject.Object.ObjectNotifySignalInfo)] :: [(Symbol, *)])

    #endif

    foreign import ccall "g_converter_get_type"
        c_g_converter_get_type :: IO GType

    instance GObject Converter where
        gobjectType _ = c_g_converter_get_type

    -- | Type class for types which can be safely cast to `Converter`, for instance with `toConverter`.
    class GObject o => IsConverter o
    #if MIN_VERSION_base(4,9,0)
    instance {-# OVERLAPPABLE #-} (GObject a, O.UnknownAncestorError Converter a) =>
        IsConverter a
    #endif
    instance IsConverter Converter
    instance GObject.Object.IsObject Converter

    -- | Cast to `Converter`, for types for which this is known to be safe. For general casts, use `Data.GI.Base.ManagedPtr.castTo`.
    toConverter :: (MonadIO m, IsConverter o) => o -> m Converter
    toConverter = liftIO . unsafeCastTo Converter

    #if ENABLE_OVERLOADING
    instance O.HasAttributeList Converter
    type instance O.AttributeList Converter = ConverterAttributeList
    type ConverterAttributeList = ('[ ] :: [(Symbol, *)])
    #endif

    #if ENABLE_OVERLOADING
    #endif

    #if ENABLE_OVERLOADING
    type family ResolveConverterMethod (t :: Symbol) (o :: *) :: * where
        ResolveConverterMethod "bindProperty" o = GObject.Object.ObjectBindPropertyMethodInfo
        ResolveConverterMethod "bindPropertyFull" o = GObject.Object.ObjectBindPropertyFullMethodInfo
        ResolveConverterMethod "convert" o = ConverterConvertMethodInfo
        ResolveConverterMethod "forceFloating" o = GObject.Object.ObjectForceFloatingMethodInfo
        ResolveConverterMethod "freezeNotify" o = GObject.Object.ObjectFreezeNotifyMethodInfo
        ResolveConverterMethod "getv" o = GObject.Object.ObjectGetvMethodInfo
        ResolveConverterMethod "isFloating" o = GObject.Object.ObjectIsFloatingMethodInfo
        ResolveConverterMethod "notify" o = GObject.Object.ObjectNotifyMethodInfo
        ResolveConverterMethod "notifyByPspec" o = GObject.Object.ObjectNotifyByPspecMethodInfo
        ResolveConverterMethod "ref" o = GObject.Object.ObjectRefMethodInfo
        ResolveConverterMethod "refSink" o = GObject.Object.ObjectRefSinkMethodInfo
        ResolveConverterMethod "reset" o = ConverterResetMethodInfo
        ResolveConverterMethod "runDispose" o = GObject.Object.ObjectRunDisposeMethodInfo
        ResolveConverterMethod "stealData" o = GObject.Object.ObjectStealDataMethodInfo
        ResolveConverterMethod "stealQdata" o = GObject.Object.ObjectStealQdataMethodInfo
        ResolveConverterMethod "thawNotify" o = GObject.Object.ObjectThawNotifyMethodInfo
        ResolveConverterMethod "unref" o = GObject.Object.ObjectUnrefMethodInfo
        ResolveConverterMethod "watchClosure" o = GObject.Object.ObjectWatchClosureMethodInfo
        ResolveConverterMethod "getData" o = GObject.Object.ObjectGetDataMethodInfo
        ResolveConverterMethod "getProperty" o = GObject.Object.ObjectGetPropertyMethodInfo
        ResolveConverterMethod "getQdata" o = GObject.Object.ObjectGetQdataMethodInfo
        ResolveConverterMethod "setData" o = GObject.Object.ObjectSetDataMethodInfo
        ResolveConverterMethod "setProperty" o = GObject.Object.ObjectSetPropertyMethodInfo
        ResolveConverterMethod l o = O.MethodResolutionFailed l o

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

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

    #endif

    -- method Converter::convert
    -- method type : OrdinaryMethod
    -- Args : [Arg {argCName = "converter", argType = TInterface (Name {namespace = "Gio", name = "Converter"}), direction = DirectionIn, mayBeNull = False, argDoc = Documentation {rawDocText = Just "a #GConverter.", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferNothing},Arg {argCName = "inbuf", argType = TCArray False (-1) 2 (TBasicType TUInt8), direction = DirectionIn, mayBeNull = True, argDoc = Documentation {rawDocText = Just "the buffer\n        containing the data to convert.", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferNothing},Arg {argCName = "inbuf_size", argType = TBasicType TUInt64, direction = DirectionIn, mayBeNull = False, argDoc = Documentation {rawDocText = Just "the number of bytes in @inbuf", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferNothing},Arg {argCName = "outbuf", argType = TCArray False (-1) 4 (TBasicType TUInt8), direction = DirectionIn, mayBeNull = True, argDoc = Documentation {rawDocText = Just "a buffer to write\n   converted data in.", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferNothing},Arg {argCName = "outbuf_size", argType = TBasicType TUInt64, direction = DirectionIn, mayBeNull = False, argDoc = Documentation {rawDocText = Just "the number of bytes in @outbuf, must be at least one", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferNothing},Arg {argCName = "flags", argType = TInterface (Name {namespace = "Gio", name = "ConverterFlags"}), direction = DirectionIn, mayBeNull = False, argDoc = Documentation {rawDocText = Just "a #GConverterFlags controlling the conversion details", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferNothing},Arg {argCName = "bytes_read", argType = TBasicType TUInt64, direction = DirectionOut, mayBeNull = False, argDoc = Documentation {rawDocText = Just "will be set to the number of bytes read from @inbuf on success", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferEverything},Arg {argCName = "bytes_written", argType = TBasicType TUInt64, direction = DirectionOut, mayBeNull = False, argDoc = Documentation {rawDocText = Just "will be set to the number of bytes written to @outbuf on success", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferEverything}]
    -- Lengths : [Arg {argCName = "outbuf_size", argType = TBasicType TUInt64, direction = DirectionIn, mayBeNull = False, argDoc = Documentation {rawDocText = Just "the number of bytes in @outbuf, must be at least one", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferNothing},Arg {argCName = "inbuf_size", argType = TBasicType TUInt64, direction = DirectionIn, mayBeNull = False, argDoc = Documentation {rawDocText = Just "the number of bytes in @inbuf", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferNothing}]
    -- returnType : Just (TInterface (Name {namespace = "Gio", name = "ConverterResult"}))
    -- throws : True
    -- Skip return : False

    foreign import ccall "g_converter_convert" g_converter_convert ::
        Ptr Converter ->                        -- converter : TInterface (Name {namespace = "Gio", name = "Converter"})
        Ptr Word8 ->                            -- inbuf : TCArray False (-1) 2 (TBasicType TUInt8)
        Word64 ->                               -- inbuf_size : TBasicType TUInt64
        Ptr Word8 ->                            -- outbuf : TCArray False (-1) 4 (TBasicType TUInt8)
        Word64 ->                               -- outbuf_size : TBasicType TUInt64
        CUInt ->                                -- flags : TInterface (Name {namespace = "Gio", name = "ConverterFlags"})
        Ptr Word64 ->                           -- bytes_read : TBasicType TUInt64
        Ptr Word64 ->                           -- bytes_written : TBasicType TUInt64
        Ptr (Ptr GError) ->                     -- error
        IO CUInt

    {- |
    This is the main operation used when converting data. It is to be called
    multiple times in a loop, and each time it will do some work, i.e.
    producing some output (in /@outbuf@/) or consuming some input (from /@inbuf@/) or
    both. If its not possible to do any work an error is returned.

    Note that a single call may not consume all input (or any input at all).
    Also a call may produce output even if given no input, due to state stored
    in the converter producing output.

    If any data was either produced or consumed, and then an error happens, then
    only the successful conversion is reported and the error is returned on the
    next call.

    A full conversion loop involves calling this method repeatedly, each time
    giving it new input and space output space. When there is no more input
    data after the data in /@inbuf@/, the flag 'GI.Gio.Flags.ConverterFlagsInputAtEnd' must be set.
    The loop will be (unless some error happens) returning 'GI.Gio.Enums.ConverterResultConverted'
    each time until all data is consumed and all output is produced, then
    'GI.Gio.Enums.ConverterResultFinished' is returned instead. Note, that 'GI.Gio.Enums.ConverterResultFinished'
    may be returned even if 'GI.Gio.Flags.ConverterFlagsInputAtEnd' is not set, for instance
    in a decompression converter where the end of data is detectable from the
    data (and there might even be other data after the end of the compressed data).

    When some data has successfully been converted /@bytesRead@/ and is set to
    the number of bytes read from /@inbuf@/, and /@bytesWritten@/ is set to indicate
    how many bytes was written to /@outbuf@/. If there are more data to output
    or consume (i.e. unless the 'GI.Gio.Flags.ConverterFlagsInputAtEnd' is specified) then
    'GI.Gio.Enums.ConverterResultConverted' is returned, and if no more data is to be output
    then 'GI.Gio.Enums.ConverterResultFinished' is returned.

    On error 'GI.Gio.Enums.ConverterResultError' is returned and /@error@/ is set accordingly.
    Some errors need special handling:

    'GI.Gio.Enums.IOErrorEnumNoSpace' is returned if there is not enough space
    to write the resulting converted data, the application should
    call the function again with a larger /@outbuf@/ to continue.

    'GI.Gio.Enums.IOErrorEnumPartialInput' is returned if there is not enough
    input to fully determine what the conversion should produce,
    and the 'GI.Gio.Flags.ConverterFlagsInputAtEnd' flag is not set. This happens for
    example with an incomplete multibyte sequence when converting text,
    or when a regexp matches up to the end of the input (and may match
    further input). It may also happen when /@inbufSize@/ is zero and
    there is no more data to produce.

    When this happens the application should read more input and then
    call the function again. If further input shows that there is no
    more data call the function again with the same data but with
    the 'GI.Gio.Flags.ConverterFlagsInputAtEnd' flag set. This may cause the conversion
    to finish as e.g. in the regexp match case (or, to fail again with
    'GI.Gio.Enums.IOErrorEnumPartialInput' in e.g. a charset conversion where the
    input is actually partial).

    After 'GI.Gio.Interfaces.Converter.converterConvert' has returned 'GI.Gio.Enums.ConverterResultFinished' the
    converter object is in an invalid state where its not allowed
    to call 'GI.Gio.Interfaces.Converter.converterConvert' anymore. At this time you can only
    free the object or call 'GI.Gio.Interfaces.Converter.converterReset' to reset it to the
    initial state.

    If the flag 'GI.Gio.Flags.ConverterFlagsFlush' is set then conversion is modified
    to try to write out all internal state to the output. The application
    has to call the function multiple times with the flag set, and when
    the available input has been consumed and all internal state has
    been produced then 'GI.Gio.Enums.ConverterResultFlushed' (or 'GI.Gio.Enums.ConverterResultFinished' if
    really at the end) is returned instead of 'GI.Gio.Enums.ConverterResultConverted'.
    This is somewhat similar to what happens at the end of the input stream,
    but done in the middle of the data.

    This has different meanings for different conversions. For instance
    in a compression converter it would mean that we flush all the
    compression state into output such that if you uncompress the
    compressed data you get back all the input data. Doing this may
    make the final file larger due to padding though. Another example
    is a regexp conversion, where if you at the end of the flushed data
    have a match, but there is also a potential longer match. In the
    non-flushed case we would ask for more input, but when flushing we
    treat this as the end of input and do the match.

    Flushing is not always possible (like if a charset converter flushes
    at a partial multibyte sequence). Converters are supposed to try
    to produce as much output as possible and then return an error
    (typically 'GI.Gio.Enums.IOErrorEnumPartialInput').

    /Since: 2.24/
    -}
    converterConvert ::
        (B.CallStack.HasCallStack, MonadIO m, IsConverter a) =>
        a
        {- ^ /@converter@/: a 'GI.Gio.Interfaces.Converter.Converter'. -}
        -> Maybe (ByteString)
        {- ^ /@inbuf@/: the buffer
            containing the data to convert. -}
        -> Maybe (ByteString)
        {- ^ /@outbuf@/: a buffer to write
       converted data in. -}
        -> [Gio.Flags.ConverterFlags]
        {- ^ /@flags@/: a 'GI.Gio.Flags.ConverterFlags' controlling the conversion details -}
        -> m ((Gio.Enums.ConverterResult, Word64, Word64))
        {- ^ __Returns:__ a 'GI.Gio.Enums.ConverterResult', 'GI.Gio.Enums.ConverterResultError' on error. /(Can throw 'Data.GI.Base.GError.GError')/ -}
    converterConvert converter inbuf outbuf flags = liftIO $ do
        let outbufSize = case outbuf of
                Nothing -> 0
                Just jOutbuf -> fromIntegral $ B.length jOutbuf
        let inbufSize = case inbuf of
                Nothing -> 0
                Just jInbuf -> fromIntegral $ B.length jInbuf
        converter' <- unsafeManagedPtrCastPtr converter
        maybeInbuf <- case inbuf of
            Nothing -> return nullPtr
            Just jInbuf -> do
                jInbuf' <- packByteString jInbuf
                return jInbuf'
        maybeOutbuf <- case outbuf of
            Nothing -> return nullPtr
            Just jOutbuf -> do
                jOutbuf' <- packByteString jOutbuf
                return jOutbuf'
        let flags' = gflagsToWord flags
        bytesRead <- allocMem :: IO (Ptr Word64)
        bytesWritten <- allocMem :: IO (Ptr Word64)
        onException (do
            result <- propagateGError $ g_converter_convert converter' maybeInbuf inbufSize maybeOutbuf outbufSize flags' bytesRead bytesWritten
            let result' = (toEnum . fromIntegral) result
            bytesRead' <- peek bytesRead
            bytesWritten' <- peek bytesWritten
            touchManagedPtr converter
            freeMem maybeInbuf
            freeMem maybeOutbuf
            freeMem bytesRead
            freeMem bytesWritten
            return (result', bytesRead', bytesWritten')
         ) (do
            freeMem maybeInbuf
            freeMem maybeOutbuf
            freeMem bytesRead
            freeMem bytesWritten
         )

    #if ENABLE_OVERLOADING
    data ConverterConvertMethodInfo
    instance (signature ~ (Maybe (ByteString) -> Maybe (ByteString) -> [Gio.Flags.ConverterFlags] -> m ((Gio.Enums.ConverterResult, Word64, Word64))), MonadIO m, IsConverter a) => O.MethodInfo ConverterConvertMethodInfo a signature where
        overloadedMethod _ = converterConvert

    #endif

    -- method Converter::reset
    -- method type : OrdinaryMethod
    -- Args : [Arg {argCName = "converter", argType = TInterface (Name {namespace = "Gio", name = "Converter"}), direction = DirectionIn, mayBeNull = False, argDoc = Documentation {rawDocText = Just "a #GConverter.", sinceVersion = Nothing}, argScope = ScopeTypeInvalid, argClosure = -1, argDestroy = -1, argCallerAllocates = False, transfer = TransferNothing}]
    -- Lengths : []
    -- returnType : Nothing
    -- throws : False
    -- Skip return : False

    foreign import ccall "g_converter_reset" g_converter_reset ::
        Ptr Converter ->                        -- converter : TInterface (Name {namespace = "Gio", name = "Converter"})
        IO ()

    {- |
    Resets all internal state in the converter, making it behave
    as if it was just created. If the converter has any internal
    state that would produce output then that output is lost.

    /Since: 2.24/
    -}
    converterReset ::
        (B.CallStack.HasCallStack, MonadIO m, IsConverter a) =>
        a
        {- ^ /@converter@/: a 'GI.Gio.Interfaces.Converter.Converter'. -}
        -> m ()
    converterReset converter = liftIO $ do
        converter' <- unsafeManagedPtrCastPtr converter
        g_converter_reset converter'
        touchManagedPtr converter
        return ()

    #if ENABLE_OVERLOADING
    data ConverterResetMethodInfo
    instance (signature ~ (m ()), MonadIO m, IsConverter a) => O.MethodInfo ConverterResetMethodInfo a signature where
        overloadedMethod _ = converterReset

    #endif