module Multimap.ByteString
  ( new
  , insert
  , lookup
  , Multimap
  ) where

import qualified Data.ByteString as B
import           Data.Word

import           Foreign.ForeignPtr
import           Foreign.Marshal.Array
import           Foreign.Ptr
import           Foreign.C.String
import           Foreign.Storable

import           Prelude hiding (length, lookup)

data    CMultimap
newtype Multimap = Multimap (ForeignPtr CMultimap)

foreign import ccall unsafe "new_mmap_str"     new_mmap :: IO (Ptr CMultimap)
foreign import ccall unsafe "&delete_mmap_str" delete_mmap
  :: FunPtr (Ptr CMultimap -> IO ())
foreign import ccall unsafe "insert_mmap_str"  insert_mmap
  :: Ptr CMultimap
  -> CString
  -> Word32
  -> Word32
  -> IO ()
foreign import ccall unsafe "lookup_mmap_str"  lookup_mmap
  :: Ptr CMultimap
  -> CString
  -> Word32
  -> IO (Ptr Word32)
foreign import ccall unsafe "free_result"      free_result
  :: Ptr Word32
  -> IO ()

new :: IO Multimap
new = do
  mm <- new_mmap
  Multimap <$> newForeignPtr delete_mmap mm

insert :: Multimap -> B.ByteString -> Word32 -> IO ()
insert (Multimap mm) k v = withForeignPtr mm $ \mm' ->
  B.useAsCStringLen k $ \(k', l) -> insert_mmap mm' k' (fromIntegral l) v

lookup :: Multimap -> B.ByteString -> IO [Word32]
lookup (Multimap mm) k = withForeignPtr mm $ \mm' -> do
  result <- B.useAsCStringLen k $ \(k', l) -> lookup_mmap mm' k' (fromIntegral l)
  length <- peek result
  array  <- peekArray (fromIntegral length) result

  free_result result
  pure $ drop 1 array