    {-# LANGUAGE ForeignFunctionInterface, EmptyDataDecls #-}
    module Database.TokyoCabinet.BDB.Cursor
        (
          CursorPutMode(..)
        , new
        , delete
        , first
        , last
        , jump
        , prev
        , next
        , put
        , out
        , key
        , val
        , BDBCUR
        ) where

    import Prelude hiding (last)

    import Database.TokyoCabinet.BDB.C
    import Database.TokyoCabinet.BDB.Cursor.C
    import qualified Database.TokyoCabinet.Storable as S

    import Foreign.ForeignPtr

    import Foreign.Marshal (alloca)
    import Foreign.Storable (peek)
    import Foreign.Marshal.Utils (maybePeek)

    data BDBCUR = BDBCUR !(ForeignPtr CUR) BDB

    unTCBDBCUR :: BDBCUR -> ForeignPtr CUR
    unTCBDBCUR (BDBCUR cur _) = cur

    new :: BDB -> IO BDBCUR
    new bdb =
        withForeignPtr (unTCBDB bdb) $ \bdb' -> do
          cur <- c_tcbdbcurnew bdb'
          flip BDBCUR bdb `fmap` newForeignPtr tcbdbcurFinalizer cur

    delete :: BDBCUR -> IO ()
    delete cur = finalizeForeignPtr (unTCBDBCUR cur)

    first :: BDBCUR -> IO Bool
    first cur = withForeignPtr (unTCBDBCUR cur) c_tcbdbcurfirst

    last :: BDBCUR -> IO Bool
    last cur = withForeignPtr (unTCBDBCUR cur) c_tcbdbcurlast

    jump :: (S.Storable k) => BDBCUR -> k -> IO Bool
    jump cur k =
        withForeignPtr (unTCBDBCUR cur) $ \cur' ->
            S.withPtrLen k $ \(kbuf, ksiz) ->
                c_tcbdbcurjump cur' kbuf ksiz

    prev :: BDBCUR -> IO Bool
    prev cur = withForeignPtr (unTCBDBCUR cur) c_tcbdbcurprev

    next :: BDBCUR -> IO Bool
    next cur = withForeignPtr (unTCBDBCUR cur) c_tcbdbcurnext

    put :: (S.Storable v) => BDBCUR -> v -> CursorPutMode -> IO Bool
    put cur v mode =
        withForeignPtr (unTCBDBCUR cur) $ \cur' ->
            S.withPtrLen v $ \(vbuf, vsiz) ->
                c_tcbdbcurput cur' vbuf vsiz (cpToCInt mode)

    out :: BDBCUR -> IO Bool
    out cur = withForeignPtr (unTCBDBCUR cur) c_tcbdbcurout

    key :: (S.Storable k) => BDBCUR -> IO (Maybe k)
    key cur =
        withForeignPtr (unTCBDBCUR cur) $ \cur' ->
            alloca $ \sizbuf -> do
              vbuf <- c_tcbdbcurkey cur' sizbuf
              vsiz <- fromIntegral `fmap` peek sizbuf
              flip maybePeek vbuf $ \vbuf' -> S.peekPtrLen (vbuf', vsiz)

    val :: (S.Storable v) => BDBCUR -> IO (Maybe v)
    val cur =
        withForeignPtr (unTCBDBCUR cur) $ \cur' ->
            alloca $ \sizbuf -> do
              vbuf <- c_tcbdbcurval cur' sizbuf
              vsiz <- fromIntegral `fmap` peek sizbuf
              flip maybePeek vbuf $ \vbuf' -> S.peekPtrLen (vbuf', vsiz)