{-# OPTIONS_HADDOCK not-home #-}
{-# LANGUAGE CApiFFI #-}

-- |
-- Module: Database.LMDB.Internal
-- Copyright: (c) 2026 Jared Tobin
-- License: MIT
-- Maintainer: Jared Tobin <jared@ppad.tech>
--
-- Raw FFI declarations and constants for the LMDB C API. This module
-- is exposed for power users; prefer "Database.LMDB" for the safe,
-- bracketed interface.

module Database.LMDB.Internal (
  -- * Opaque handles
    MDB_env
  , MDB_txn
  , MDB_dbi
  , MDB_cursor

  -- * Value pair
  , MDB_val(..)

  -- * Environment flags
  , _MDB_NOSUBDIR
  , _MDB_NOSYNC
  , _MDB_RDONLY
  , _MDB_NOMETASYNC
  , _MDB_NOTLS

  -- * Database open flags
  , _MDB_CREATE

  -- * Put flags
  , _MDB_NOOVERWRITE
  , _MDB_APPEND

  -- * Cursor operations
  , _MDB_FIRST
  , _MDB_LAST
  , _MDB_NEXT
  , _MDB_PREV
  , _MDB_SET
  , _MDB_SET_RANGE
  , _MDB_GET_CURRENT

  -- * Error codes
  , _MDB_SUCCESS
  , _MDB_KEYEXIST
  , _MDB_NOTFOUND
  , _MDB_MAP_FULL
  , _MDB_CORRUPTED
  , _MDB_PANIC
  , _MDB_VERSION_MISMATCH
  , _MDB_INVALID
  , _MDB_BAD_TXN
  , _MDB_BAD_VALSIZE
  , _MDB_BAD_DBI

  -- * Environment lifecycle
  , mdb_env_create
  , mdb_env_open
  , mdb_env_close
  , mdb_env_set_mapsize
  , mdb_env_set_maxdbs
  , mdb_env_sync

  -- * Transactions
  , mdb_txn_begin
  , mdb_txn_commit
  , mdb_txn_abort

  -- * Databases
  , mdb_dbi_open
  , mdb_dbi_close

  -- * Key-value operations
  , mdb_get
  , mdb_put
  , mdb_del

  -- * Cursors
  , mdb_cursor_open
  , mdb_cursor_close
  , mdb_cursor_get

  -- * Diagnostics
  , mdb_strerror
  ) where

import Foreign.C.String (CString)
import Foreign.C.Types
  (CInt(..), CUInt(..), CSize(..), CChar(..))
import Foreign.Ptr (Ptr)
import Foreign.Storable (Storable(..))
import System.Posix.Types (CMode(..))

-- opaque handles -------------------------------------------------------------

data MDB_env
data MDB_txn
data MDB_cursor

-- | LMDB's database identifier is a plain @unsigned int@ value type,
--   not a pointer.
type MDB_dbi = CUInt

-- value pair -----------------------------------------------------------------

-- | The key-or-value pair LMDB passes across the FFI boundary.
data MDB_val = MDB_val
  { MDB_val -> CSize
mvSize :: !CSize
  , MDB_val -> Ptr CChar
mvData :: !(Ptr CChar)
  }

instance Storable MDB_val where
  sizeOf :: MDB_val -> Int
sizeOf    MDB_val
_ = Int
2 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Ptr () -> Int
forall a. Storable a => a -> Int
sizeOf (Ptr ()
forall a. HasCallStack => a
undefined :: Ptr ())
  {-# INLINE sizeOf #-}
  alignment :: MDB_val -> Int
alignment MDB_val
_ = Ptr () -> Int
forall a. Storable a => a -> Int
alignment   (Ptr ()
forall a. HasCallStack => a
undefined :: Ptr ())
  {-# INLINE alignment #-}
  peek :: Ptr MDB_val -> IO MDB_val
peek Ptr MDB_val
p = do
    sz <- Ptr MDB_val -> Int -> IO CSize
forall b. Ptr b -> Int -> IO CSize
forall a b. Storable a => Ptr b -> Int -> IO a
peekByteOff Ptr MDB_val
p Int
0
    pd <- peekByteOff p (sizeOf (undefined :: Ptr ()))
    pure (MDB_val sz pd)
  {-# INLINE peek #-}
  poke :: Ptr MDB_val -> MDB_val -> IO ()
poke Ptr MDB_val
p (MDB_val CSize
sz Ptr CChar
pd) = do
    Ptr MDB_val -> Int -> CSize -> IO ()
forall b. Ptr b -> Int -> CSize -> IO ()
forall a b. Storable a => Ptr b -> Int -> a -> IO ()
pokeByteOff Ptr MDB_val
p Int
0 CSize
sz
    Ptr MDB_val -> Int -> Ptr CChar -> IO ()
forall b. Ptr b -> Int -> Ptr CChar -> IO ()
forall a b. Storable a => Ptr b -> Int -> a -> IO ()
pokeByteOff Ptr MDB_val
p (Ptr () -> Int
forall a. Storable a => a -> Int
sizeOf (Ptr ()
forall a. HasCallStack => a
undefined :: Ptr ())) Ptr CChar
pd
  {-# INLINE poke #-}

-- environment flags ----------------------------------------------------------

_MDB_NOSUBDIR, _MDB_NOSYNC, _MDB_RDONLY, _MDB_NOMETASYNC, _MDB_NOTLS
  :: CUInt
_MDB_NOSUBDIR :: CUInt
_MDB_NOSUBDIR    = CUInt
0x4000
_MDB_NOSYNC :: CUInt
_MDB_NOSYNC      = CUInt
0x10000
_MDB_RDONLY :: CUInt
_MDB_RDONLY      = CUInt
0x20000
_MDB_NOMETASYNC :: CUInt
_MDB_NOMETASYNC  = CUInt
0x40000
_MDB_NOTLS :: CUInt
_MDB_NOTLS       = CUInt
0x200000

-- database open flags --------------------------------------------------------

_MDB_CREATE :: CUInt
_MDB_CREATE :: CUInt
_MDB_CREATE = CUInt
0x40000

-- put flags ------------------------------------------------------------------

_MDB_NOOVERWRITE, _MDB_APPEND :: CUInt
_MDB_NOOVERWRITE :: CUInt
_MDB_NOOVERWRITE = CUInt
0x10
_MDB_APPEND :: CUInt
_MDB_APPEND      = CUInt
0x20000

-- cursor ops -----------------------------------------------------------------

-- These mirror the enum MDB_cursor_op declared in lmdb.h. We only
-- bind the operations the safe wrapper currently uses; add more as
-- needed.

_MDB_FIRST, _MDB_LAST, _MDB_NEXT, _MDB_PREV
  , _MDB_SET, _MDB_SET_RANGE, _MDB_GET_CURRENT :: CInt
_MDB_FIRST :: CInt
_MDB_FIRST       = CInt
0
_MDB_LAST :: CInt
_MDB_LAST        = CInt
6
_MDB_NEXT :: CInt
_MDB_NEXT        = CInt
8
_MDB_PREV :: CInt
_MDB_PREV        = CInt
12
_MDB_SET :: CInt
_MDB_SET         = CInt
15
_MDB_SET_RANGE :: CInt
_MDB_SET_RANGE   = CInt
17  -- index per MDB_cursor_op enum (post-DUP entries)
_MDB_GET_CURRENT :: CInt
_MDB_GET_CURRENT = CInt
4

-- error codes ----------------------------------------------------------------

_MDB_SUCCESS, _MDB_KEYEXIST, _MDB_NOTFOUND, _MDB_MAP_FULL
  , _MDB_CORRUPTED, _MDB_PANIC, _MDB_VERSION_MISMATCH, _MDB_INVALID
  , _MDB_BAD_TXN, _MDB_BAD_VALSIZE, _MDB_BAD_DBI :: CInt
_MDB_SUCCESS :: CInt
_MDB_SUCCESS          = CInt
0
_MDB_KEYEXIST :: CInt
_MDB_KEYEXIST         = -CInt
30799
_MDB_NOTFOUND :: CInt
_MDB_NOTFOUND         = -CInt
30798
_MDB_CORRUPTED :: CInt
_MDB_CORRUPTED        = -CInt
30796
_MDB_PANIC :: CInt
_MDB_PANIC            = -CInt
30795
_MDB_VERSION_MISMATCH :: CInt
_MDB_VERSION_MISMATCH = -CInt
30794
_MDB_INVALID :: CInt
_MDB_INVALID          = -CInt
30793
_MDB_MAP_FULL :: CInt
_MDB_MAP_FULL         = -CInt
30792
_MDB_BAD_TXN :: CInt
_MDB_BAD_TXN          = -CInt
30782
_MDB_BAD_VALSIZE :: CInt
_MDB_BAD_VALSIZE      = -CInt
30781
_MDB_BAD_DBI :: CInt
_MDB_BAD_DBI          = -CInt
30780

-- foreign imports ------------------------------------------------------------

-- environment lifecycle

foreign import capi "lmdb.h mdb_env_create"
  mdb_env_create :: Ptr (Ptr MDB_env) -> IO CInt

foreign import capi "lmdb.h mdb_env_open"
  mdb_env_open
    :: Ptr MDB_env -> CString -> CUInt -> CMode -> IO CInt

foreign import capi "lmdb.h mdb_env_close"
  mdb_env_close :: Ptr MDB_env -> IO ()

foreign import capi "lmdb.h mdb_env_set_mapsize"
  mdb_env_set_mapsize :: Ptr MDB_env -> CSize -> IO CInt

foreign import capi "lmdb.h mdb_env_set_maxdbs"
  mdb_env_set_maxdbs :: Ptr MDB_env -> MDB_dbi -> IO CInt

foreign import capi "lmdb.h mdb_env_sync"
  mdb_env_sync :: Ptr MDB_env -> CInt -> IO CInt

-- transactions

foreign import capi "lmdb.h mdb_txn_begin"
  mdb_txn_begin
    :: Ptr MDB_env
    -> Ptr MDB_txn      -- parent (NULL for top-level)
    -> CUInt            -- flags
    -> Ptr (Ptr MDB_txn)
    -> IO CInt

foreign import capi "lmdb.h mdb_txn_commit"
  mdb_txn_commit :: Ptr MDB_txn -> IO CInt

foreign import capi "lmdb.h mdb_txn_abort"
  mdb_txn_abort :: Ptr MDB_txn -> IO ()

-- databases

foreign import capi "lmdb.h mdb_dbi_open"
  mdb_dbi_open
    :: Ptr MDB_txn
    -> CString          -- name; NULL for the default unnamed db
    -> CUInt            -- flags
    -> Ptr MDB_dbi
    -> IO CInt

foreign import capi "lmdb.h mdb_dbi_close"
  mdb_dbi_close :: Ptr MDB_env -> MDB_dbi -> IO ()

-- key-value ops

foreign import capi "lmdb.h mdb_get"
  mdb_get
    :: Ptr MDB_txn -> MDB_dbi -> Ptr MDB_val -> Ptr MDB_val -> IO CInt

foreign import capi "lmdb.h mdb_put"
  mdb_put
    :: Ptr MDB_txn
    -> MDB_dbi
    -> Ptr MDB_val      -- key
    -> Ptr MDB_val      -- data
    -> CUInt            -- flags
    -> IO CInt

foreign import capi "lmdb.h mdb_del"
  mdb_del
    :: Ptr MDB_txn
    -> MDB_dbi
    -> Ptr MDB_val      -- key
    -> Ptr MDB_val      -- data; NULL except for MDB_DUPSORT
    -> IO CInt

-- cursors

foreign import capi "lmdb.h mdb_cursor_open"
  mdb_cursor_open
    :: Ptr MDB_txn -> MDB_dbi -> Ptr (Ptr MDB_cursor) -> IO CInt

foreign import capi "lmdb.h mdb_cursor_close"
  mdb_cursor_close :: Ptr MDB_cursor -> IO ()

foreign import capi "lmdb.h mdb_cursor_get"
  mdb_cursor_get
    :: Ptr MDB_cursor
    -> Ptr MDB_val      -- key (in/out)
    -> Ptr MDB_val      -- data (out)
    -> CInt             -- MDB_cursor_op
    -> IO CInt

-- diagnostics

foreign import capi "lmdb.h mdb_strerror"
  mdb_strerror :: CInt -> IO CString