    -- | Interface to TC's Abstract DataBase. See also,
    -- <http://tokyocabinet.sourceforge.net/spex-en.html#tcadbapi> for details
    module Database.TokyoCabinet.ADB
        (
        -- $doc
          ADB
        , ECODE(..)
        , new
        , delete
        , open
        , close
        , put
        , putkeep
        , putcat
        , out
        , get
        , vsiz
        , iterinit
        , iternext
        , fwmkeys
        , addint
        , adddouble
        , sync
        , optimize
        , vanish
        , copy
        , tranbegin
        , trancommit
        , tranabort
        , path
        , rnum
        , size
        , misc
        ) where

    import Data.Word

    import Foreign.C.String
    import Foreign.ForeignPtr

    import Database.TokyoCabinet.ADB.C
    import Database.TokyoCabinet.Error
    import Database.TokyoCabinet.Internal
    import Database.TokyoCabinet.Storable
    import Database.TokyoCabinet.Sequence

    -- $doc
    -- Example
    --
    -- @
    --   import Control.Monad
    --   import Database.TokyoCabinet.ADB
    -- @
    --
    -- @
    --   main = do adb <- new
    --             -- open the abstract database object
    --             -- \"+\" means that the database will be an on-memory tree database
    --             open adb \"+\" >>= err adb \"open failed\"
    --             -- store records
    --             puts adb [(\"foo\", \"hop\"), (\"bar\", \"step\"), (\"baz\", \"jump\")] >>=
    --                      err adb \"put failed\" . (all id)
    --             -- retrieve records
    --             get_print adb \"foo\"
    --             -- traverse records
    --             iterinit adb
    --             iter adb >>= mapM_ (\k -> putStr (k++\":\") >> get_print adb k)
    --             -- close the database
    --             close adb >>= err adb \"close failed\"
    --       where
    --         puts :: ADB -> [(String, String)] -> IO [Bool]
    --         puts adb = mapM (uncurry $ put adb)
    -- @
    --
    -- @
    --         get_print :: ADB -> String -> IO ()
    --         get_print adb key = get adb key >>=
    --                             maybe (error \"something goes wrong\") putStrLn
    -- @
    --
    -- @
    --         err :: ADB -> String -> Bool -> IO ()
    --         err adb msg = flip unless $ error msg
    -- @
    --
    -- @
    --         iter :: ADB -> IO [String]
    --         iter adb = iternext adb >>=
    --                    maybe (return []) (\x -> return . (x:) =<< iter adb)
    -- @
    --

    data ADB = ADB { unTCADB :: !(ForeignPtr ADB') }

    -- | Create a Abstract database object.
    new :: IO ADB
    new = ADB `fmap` (c_tcadbnew >>= newForeignPtr tcadbFinalizer)

    -- | Free ADB resource forcibly.
    -- ADB is kept by ForeignPtr, so Haskell runtime GC cleans up memory for
    -- almost situation. Most always, you don't need to call this.
    -- After call this, you must not touch ADB object. Its behavior is undefined.
    delete :: ADB -> IO ()
    delete adb = finalizeForeignPtr (unTCADB adb)

    -- | Open an abstract dataabse.
    open :: ADB -> String -> IO Bool
    open adb name = withForeignPtr (unTCADB adb) $ (withCString name) . c_tcadbopen

    -- | Close an abstract database object.
    close :: ADB -> IO Bool
    close adb = withForeignPtr (unTCADB adb) c_tcadbclose

    -- | Stora a record into an abstract database object.
    put :: (Storable k, Storable v) => ADB -> k -> v -> IO Bool
    put = putHelper c_tcadbput unTCADB

    -- | Store a new record into an abstract database object.
    putkeep :: (Storable k, Storable v) => ADB -> k -> v -> IO Bool
    putkeep = putHelper c_tcadbputkeep unTCADB

    -- | Concatenate a value at the end of the existing record in an
    -- abstract database object.
    putcat :: (Storable k, Storable v) => ADB -> k -> v -> IO Bool
    putcat = putHelper c_tcadbputcat unTCADB

    -- | Remove a record of an abstract database object.
    out :: (Storable k) => ADB -> k -> IO Bool
    out = outHelper c_tcadbout unTCADB

    -- | Retrieve a record in an abstract database object.
    get :: (Storable k, Storable v) => ADB -> k -> IO (Maybe v)
    get = getHelper c_tcadbget unTCADB

    -- | Get the size of the value of a record in an abstract database object.
    vsiz :: (Storable k) => ADB -> k -> IO (Maybe Int)
    vsiz = vsizHelper c_tcadbvsiz unTCADB

    -- | Initialize the iterator of an abstract database object.
    iterinit :: ADB -> IO Bool
    iterinit adb = withForeignPtr (unTCADB adb) c_tcadbiterinit

    -- | Get the next key of the iterator of an abstract database object.
    iternext :: (Storable k) => ADB -> IO (Maybe k)
    iternext = iternextHelper c_tcadbiternext unTCADB

    -- | Get forward matching keys in an abstract database object.
    fwmkeys :: (Storable k1, Storable k2, Sequence q) =>
               ADB -> k1 -> Int -> IO (q k2)
    fwmkeys = fwmHelper c_tcadbfwmkeys unTCADB

    -- | Add an integer to a record in an abstract database object.
    addint :: (Storable k) => ADB -> k -> Int -> IO (Maybe Int)
    addint = addHelper c_tcadbaddint unTCADB fromIntegral fromIntegral (== cINT_MIN)

    -- | Add a real number to a record in an abstract database object.
    adddouble :: (Storable k) => ADB -> k -> Double -> IO (Maybe Double)
    adddouble = addHelper c_tcadbadddouble unTCADB realToFrac realToFrac isNaN

    -- | Synchronize updated contents of an abstract database object with
    -- the file and the device.
    sync :: ADB -> IO Bool
    sync adb = withForeignPtr (unTCADB adb) c_tcadbsync

    -- | Optimize the storage of an abstract database object.
    optimize :: ADB -> String -> IO Bool
    optimize adb params =
        withForeignPtr (unTCADB adb) $ (withCString params) . c_tcadboptimize

    -- | Remove all records of an abstract database object.
    vanish :: ADB -> IO Bool
    vanish adb = withForeignPtr (unTCADB adb) c_tcadbvanish

    -- | Copy the database file of an abstract database object.
    copy :: ADB -> String -> IO Bool
    copy = copyHelper c_tcadbcopy unTCADB

    -- | Begin the transaction of an abstract database object.
    tranbegin :: ADB -> IO Bool
    tranbegin adb = withForeignPtr (unTCADB adb) c_tcadbtranbegin

    -- | Commit the transaction of an abstract database object.
    trancommit :: ADB -> IO Bool
    trancommit adb = withForeignPtr (unTCADB adb) c_tcadbtrancommit

    -- | Abort the transaction of an abstract database object.
    tranabort :: ADB -> IO Bool
    tranabort adb = withForeignPtr (unTCADB adb) c_tcadbtranabort

    -- | Get the file path of an abstract database object.
    path :: ADB -> IO (Maybe String)
    path = pathHelper c_tcadbpath unTCADB

    -- | Get the number of records of an abstract database object.
    rnum :: ADB -> IO Word64
    rnum adb = withForeignPtr (unTCADB adb) c_tcadbrnum

    -- | Get the size of the database of an abstract database object.
    size :: ADB -> IO Word64
    size adb = withForeignPtr (unTCADB adb) c_tcadbsize

    -- | Call a versatile function for miscellaneous operations of an
    -- abstract database object.
    misc :: (Storable a, Storable b, Sequence q1, Sequence q2) =>
            ADB -> String -> q1 a -> IO (q2 b)
    misc adb name args =
        withForeignPtr (unTCADB adb) $ \adb' ->
            withCString name $ \name' ->
                withList args $ \args' -> do
                  ret <- c_tcadbmisc adb' name' args'
                  peekList' ret