{-# 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)