    -- | Interface to Hash based DBM. See also,
    -- <http://tokyocabinet.sourceforge.net/spex-en.html#tchdbapi> for details
    module Database.TokyoCabinet.HDB
        (
        -- $doc
        -- * Constructors
          HDB
        , ECODE(..)
        , OpenMode(..)
        , TuningOption(..)
        -- * Basic API (tokyocabinet.idl compliant)
        , new
        , delete
        , ecode
        , errmsg
        , tune
        , setcache
        , setxmsiz
        , open
        , close
        , put
        , putkeep
        , putcat
        , putasync
        , out
        , get
        , vsiz
        , iterinit
        , iternext
        , fwmkeys
        , addint
        , adddouble
        , sync
        , optimize
        , vanish
        , copy
        , tranbegin
        , trancommit
        , tranabort
        , path
        , rnum
        , fsiz
        )
        where

    import Foreign.ForeignPtr

    import Data.Int
    import Data.Word

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

    -- $doc
    -- Example
    --
    -- @
    --    import Control.Monad
    --    import Database.TokyoCabinet.HDB
    -- @
    --
    -- @
    --    main = do hdb <- new
    --              -- open the database
    --              open hdb \"casket.tch\" [OWRITER, OCREAT] >>= err hdb
    --              -- store records
    --              puts hdb [(\"foo\", \"hop\"), (\"bar\", \"step\"), (\"baz\", \"jump\")] >>=
    --                       err hdb . (all id)
    --              -- retrieve records
    --              get_print hdb \"foo\"
    --              -- traverse records
    --              iterinit hdb
    --              iter hdb >>= mapM_ (\k -> putStr (k++\":\") >> get_print hdb k)
    --              -- close the database
    --              close hdb >>= err hdb
    --        where
    --          puts :: HDB -> [(String, String)] -> IO [Bool]
    --          puts hdb = mapM (uncurry $ put hdb)
    -- @
    --
    -- @
    --          get_print :: HDB -> String -> IO ()
    --          get_print hdb key = get hdb key >>=
    --                              maybe (error \"something goes wrong\") putStrLn
    -- @
    --
    -- @
    --          err :: HDB -> Bool -> IO ()
    --          err hdb = flip unless $ ecode hdb >>= error . show
    -- @
    --
    -- @
    --          iter :: HDB -> IO [String]
    --          iter hdb = iternext hdb >>=
    --                     maybe (return []) (\x -> return . (x:) =<< iter hdb)
    -- @
    --

    data HDB = HDB { unTCHDB :: !(ForeignPtr HDB') }

    -- | Create a Hash database object.
    new :: IO HDB
    new = HDB `fmap` (c_tchdbnew >>= newForeignPtr tchdbFinalizer)

    -- | Free HDB resource forcibly.
    -- HDB 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 HDB object. Its behavior is undefined.
    delete :: HDB -> IO ()
    delete hdb = finalizeForeignPtr (unTCHDB hdb)

    -- | Return the last happened error code.
    ecode :: HDB -> IO ECODE
    ecode hdb = cintToError `fmap` withForeignPtr (unTCHDB hdb) c_tchdbecode

    -- | Set the tuning parameters.
    tune :: HDB   -- ^ HDB object
         -> Int64 -- ^ the number of elements of the bucket array.
         -> Int8  -- ^ the size of record alignment by power of 2.
         -> Int8  -- ^ the maximum number of elements of the free block
                  -- pool by power of 2.
         -> [TuningOption] -- ^ tuning options.
         -> IO Bool -- ^ if successful, the return value is True.
    tune hdb bnum apow fpow options =
        withForeignPtr (unTCHDB hdb) $ \p ->
            c_tchdbtune p bnum apow fpow (combineTuningOption options)

    -- | Set the caching parameters.
    setcache :: HDB   -- ^ HDB object.
             -> Int32   -- ^ the maximum number of records to be cached.
             -> IO Bool -- ^ if successful, the return value is True.
    setcache hdb rcnum = withForeignPtr (unTCHDB hdb) (flip c_tchdbsetcache rcnum)

    -- | Set the size of extra mapped memory.
    setxmsiz :: HDB -> Int64 -> IO Bool
    setxmsiz hdb xmsiz = withForeignPtr (unTCHDB hdb) (flip c_tchdbsetxmsiz xmsiz)

    -- | Open a database file.
    open :: HDB -> String -> [OpenMode] -> IO Bool
    open = openHelper c_tchdbopen unTCHDB combineOpenMode

    -- | Close the database file.
    close :: HDB -> IO Bool
    close hdb = withForeignPtr (unTCHDB hdb) c_tchdbclose

    -- | Stora a record (key-value pair) on HDB.  Key and value type must
    -- be instance of Storable class.  Usually, we can use `String',
    -- `ByteString' for key, `String', `ByteString', `Int', `Double' for
    -- value.
    put :: (Storable k, Storable v) => HDB -> k -> v -> IO Bool
    put = putHelper c_tchdbput unTCHDB

    -- | Store a new record. If a record with the same key exists in the
    -- database, this function has no effect.
    putkeep :: (Storable k, Storable v) => HDB -> k -> v -> IO Bool
    putkeep = putHelper c_tchdbputkeep unTCHDB

    -- | Concatenate a value at the end of the existing record.
    putcat :: (Storable k, Storable v) => HDB -> k -> v -> IO Bool
    putcat = putHelper c_tchdbputcat unTCHDB

    -- | Store a record into a hash database object in asynchronous fashion.
    putasync :: (Storable k, Storable v) => HDB -> k -> v -> IO Bool
    putasync = putHelper c_tchdbputasync unTCHDB

    -- | Delete a record.
    out :: (Storable k) => HDB -> k -> IO Bool
    out = outHelper c_tchdbout unTCHDB

    -- | Return the value of record.
    get :: (Storable k, Storable v) => HDB -> k -> IO (Maybe v)
    get = getHelper c_tchdbget unTCHDB

    -- | Return the byte size of value in a record.
    vsiz :: (Storable k) => HDB -> k -> IO (Maybe Int)
    vsiz = vsizHelper c_tchdbvsiz unTCHDB

    -- | Initialize the iterator of a HDB object.
    iterinit :: HDB -> IO Bool
    iterinit hdb = withForeignPtr (unTCHDB hdb) c_tchdbiterinit

    -- | Return the next key of the iterator of a HDB object.
    iternext :: (Storable k) => HDB -> IO (Maybe k)
    iternext = iternextHelper c_tchdbiternext unTCHDB

    -- | Return list of forward matched keys.
    fwmkeys :: (Storable k1, Storable k2, Sequence q) =>
               HDB -> k1 -> Int -> IO (q k2)
    fwmkeys = fwmHelper c_tchdbfwmkeys unTCHDB

    -- | Increment the corresponding value. (The value specified by a key
    -- is treated as integer.)
    addint :: (Storable k) => HDB -> k -> Int -> IO (Maybe Int)
    addint = addHelper c_tchdbaddint unTCHDB fromIntegral fromIntegral (== cINT_MIN)

    -- | Increment the corresponding value. (The value specified by a key
    -- is treated as double.)
    adddouble :: (Storable k) => HDB -> k -> Double -> IO (Maybe Double)
    adddouble = addHelper c_tchdbadddouble unTCHDB realToFrac realToFrac isNaN

    -- | Synchronize updated contents of a database object with the file
    -- and the device.
    sync :: HDB -> IO Bool
    sync hdb = withForeignPtr (unTCHDB hdb) c_tchdbsync

    -- |  Optimize the file of a Hash database object.
    optimize :: HDB   -- ^ HDB object
             -> Int64 -- ^ the number of elements of the bucket array.
             -> Int8  -- ^ the size of record alignment by power of 2.
             -> Int8  -- ^ the maximum number of elements of the free block
                      -- pool by power of 2.
             -> [TuningOption] -- ^ tuning options.
             -> IO Bool -- ^ if successful, the return value is True.
    optimize hdb bnum apow fpow options =
        withForeignPtr (unTCHDB hdb) $ \p ->
            c_tchdboptimize p bnum apow fpow (combineTuningOption options)

    -- | Delete all records.
    vanish :: HDB -> IO Bool
    vanish hdb = withForeignPtr (unTCHDB hdb) c_tchdbvanish

    -- | Copy the database file.
    copy :: HDB -> String -> IO Bool
    copy = copyHelper c_tchdbcopy unTCHDB

    -- | Begin the transaction.
    tranbegin :: HDB -> IO Bool
    tranbegin hdb = withForeignPtr (unTCHDB hdb) c_tchdbtranbegin

    -- | Commit the transaction.
    trancommit :: HDB -> IO Bool
    trancommit hdb = withForeignPtr (unTCHDB hdb) c_tchdbtrancommit

    -- | Abort the transaction.
    tranabort :: HDB -> IO Bool
    tranabort hdb = withForeignPtr (unTCHDB hdb) c_tchdbtranabort

    -- | Return the file path of currentry opened database.
    path :: HDB -> IO (Maybe String)
    path = pathHelper c_tchdbpath unTCHDB

    -- | Return the number of records in the database.
    rnum :: HDB -> IO Word64
    rnum hdb = withForeignPtr (unTCHDB hdb) c_tchdbrnum

    -- | Return the size of the database file.
    fsiz :: HDB -> IO Word64
    fsiz hdb = withForeignPtr (unTCHDB hdb) c_tchdbfsiz