-- GENERATED by C->Haskell Compiler, version 0.28.6 Switcheroo, 25 November 2017 (Haskell)
-- Edit the ORIGNAL .chs file instead!


{-# LINE 1 "src/Notmuch/Binding.chs" #-}
-- Copyright (C) 2014, 2017  Fraser Tweedale
--
-- hs-notmuch is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program.  If not, see <http://www.gnu.org/licenses/>.

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}

module Notmuch.Binding where
import qualified Foreign.C.Types as C2HSImp
import qualified Foreign.ForeignPtr as C2HSImp
import qualified Foreign.Ptr as C2HSImp



import Control.Monad ((>=>), (<=<), void)
import Control.Monad.Except (MonadError(..))
import Control.Monad.IO.Class (MonadIO(..))
import Data.Coerce (coerce)
import Data.Foldable (traverse_)
import Data.Functor (($>))
import Data.Functor.Identity (Identity(..))
import Data.Proxy
import GHC.TypeLits (Nat, type (<=), type (+), type (-))

import Notmuch.Tag
import Notmuch.Util



{-# LINE 41 "src/Notmuch/Binding.chs" #-}


import Foreign
  ( ForeignPtr, Ptr, castForeignPtr
  , alloca, newForeignPtr, nullFunPtr, nullPtr, peek
  , touchForeignPtr
  )
import Foreign.C
import Foreign.Storable (Storable)
import qualified System.IO.Unsafe

import qualified Data.ByteString as B

import Notmuch.Talloc


-- | @Message-Id@ header value.
type MessageId = B.ByteString

-- | Thread identifier generated and used by /libnotmuch/.
type ThreadId = B.ByteString

--
-- BINDING API
--

data Status = StatusSuccess
            | StatusOutOfMemory
            | StatusReadOnlyDatabase
            | StatusXapianException
            | StatusFileError
            | StatusFileNotEmail
            | StatusDuplicateMessageId
            | StatusNullPointer
            | StatusTagTooLong
            | StatusUnbalancedFreezeThaw
            | StatusUnbalancedAtomic
            | StatusUnsupportedOperation
            | StatusUpgradeRequired
            | StatusPathError
            | StatusIgnored
            | StatusIllegalArgument
            | StatusMalformedCryptoProtocol
            | StatusFailedCryptoContextCreation
            | StatusUnknownCryptoProtocol
            | StatusLastStatus
  deriving (Eq)
instance Enum Status where
  succ StatusSuccess = StatusOutOfMemory
  succ StatusOutOfMemory = StatusReadOnlyDatabase
  succ StatusReadOnlyDatabase = StatusXapianException
  succ StatusXapianException = StatusFileError
  succ StatusFileError = StatusFileNotEmail
  succ StatusFileNotEmail = StatusDuplicateMessageId
  succ StatusDuplicateMessageId = StatusNullPointer
  succ StatusNullPointer = StatusTagTooLong
  succ StatusTagTooLong = StatusUnbalancedFreezeThaw
  succ StatusUnbalancedFreezeThaw = StatusUnbalancedAtomic
  succ StatusUnbalancedAtomic = StatusUnsupportedOperation
  succ StatusUnsupportedOperation = StatusUpgradeRequired
  succ StatusUpgradeRequired = StatusPathError
  succ StatusPathError = StatusIgnored
  succ StatusIgnored = StatusIllegalArgument
  succ StatusIllegalArgument = StatusMalformedCryptoProtocol
  succ StatusMalformedCryptoProtocol = StatusFailedCryptoContextCreation
  succ StatusFailedCryptoContextCreation = StatusUnknownCryptoProtocol
  succ StatusUnknownCryptoProtocol = StatusLastStatus
  succ StatusLastStatus = error "Status.succ: StatusLastStatus has no successor"

  pred StatusOutOfMemory = StatusSuccess
  pred StatusReadOnlyDatabase = StatusOutOfMemory
  pred StatusXapianException = StatusReadOnlyDatabase
  pred StatusFileError = StatusXapianException
  pred StatusFileNotEmail = StatusFileError
  pred StatusDuplicateMessageId = StatusFileNotEmail
  pred StatusNullPointer = StatusDuplicateMessageId
  pred StatusTagTooLong = StatusNullPointer
  pred StatusUnbalancedFreezeThaw = StatusTagTooLong
  pred StatusUnbalancedAtomic = StatusUnbalancedFreezeThaw
  pred StatusUnsupportedOperation = StatusUnbalancedAtomic
  pred StatusUpgradeRequired = StatusUnsupportedOperation
  pred StatusPathError = StatusUpgradeRequired
  pred StatusIgnored = StatusPathError
  pred StatusIllegalArgument = StatusIgnored
  pred StatusMalformedCryptoProtocol = StatusIllegalArgument
  pred StatusFailedCryptoContextCreation = StatusMalformedCryptoProtocol
  pred StatusUnknownCryptoProtocol = StatusFailedCryptoContextCreation
  pred StatusLastStatus = StatusUnknownCryptoProtocol
  pred StatusSuccess = error "Status.pred: StatusSuccess has no predecessor"

  enumFromTo from to = go from
    where
      end = fromEnum to
      go v = case compare (fromEnum v) end of
                 LT -> v : go (succ v)
                 EQ -> [v]
                 GT -> []

  enumFrom from = enumFromTo from StatusLastStatus

  fromEnum StatusSuccess = 0
  fromEnum StatusOutOfMemory = 1
  fromEnum StatusReadOnlyDatabase = 2
  fromEnum StatusXapianException = 3
  fromEnum StatusFileError = 4
  fromEnum StatusFileNotEmail = 5
  fromEnum StatusDuplicateMessageId = 6
  fromEnum StatusNullPointer = 7
  fromEnum StatusTagTooLong = 8
  fromEnum StatusUnbalancedFreezeThaw = 9
  fromEnum StatusUnbalancedAtomic = 10
  fromEnum StatusUnsupportedOperation = 11
  fromEnum StatusUpgradeRequired = 12
  fromEnum StatusPathError = 13
  fromEnum StatusIgnored = 14
  fromEnum StatusIllegalArgument = 15
  fromEnum StatusMalformedCryptoProtocol = 16
  fromEnum StatusFailedCryptoContextCreation = 17
  fromEnum StatusUnknownCryptoProtocol = 18
  fromEnum StatusLastStatus = 19

  toEnum 0 = StatusSuccess
  toEnum 1 = StatusOutOfMemory
  toEnum 2 = StatusReadOnlyDatabase
  toEnum 3 = StatusXapianException
  toEnum 4 = StatusFileError
  toEnum 5 = StatusFileNotEmail
  toEnum 6 = StatusDuplicateMessageId
  toEnum 7 = StatusNullPointer
  toEnum 8 = StatusTagTooLong
  toEnum 9 = StatusUnbalancedFreezeThaw
  toEnum 10 = StatusUnbalancedAtomic
  toEnum 11 = StatusUnsupportedOperation
  toEnum 12 = StatusUpgradeRequired
  toEnum 13 = StatusPathError
  toEnum 14 = StatusIgnored
  toEnum 15 = StatusIllegalArgument
  toEnum 16 = StatusMalformedCryptoProtocol
  toEnum 17 = StatusFailedCryptoContextCreation
  toEnum 18 = StatusUnknownCryptoProtocol
  toEnum 19 = StatusLastStatus
  toEnum unmatched = error ("Status.toEnum: Cannot match " ++ show unmatched)

{-# LINE 67 "src/Notmuch/Binding.chs" #-}

data DatabaseMode = DatabaseModeReadOnly
                  | DatabaseModeReadWrite
instance Enum DatabaseMode where
  succ DatabaseModeReadOnly = DatabaseModeReadWrite
  succ DatabaseModeReadWrite = error "DatabaseMode.succ: DatabaseModeReadWrite has no successor"

  pred DatabaseModeReadWrite = DatabaseModeReadOnly
  pred DatabaseModeReadOnly = error "DatabaseMode.pred: DatabaseModeReadOnly has no predecessor"

  enumFromTo from to = go from
    where
      end = fromEnum to
      go v = case compare (fromEnum v) end of
                 LT -> v : go (succ v)
                 EQ -> [v]
                 GT -> []

  enumFrom from = enumFromTo from DatabaseModeReadWrite

  fromEnum DatabaseModeReadOnly = 0
  fromEnum DatabaseModeReadWrite = 1

  toEnum 0 = DatabaseModeReadOnly
  toEnum 1 = DatabaseModeReadWrite
  toEnum unmatched = error ("DatabaseMode.toEnum: Cannot match " ++ show unmatched)

{-# LINE 68 "src/Notmuch/Binding.chs" #-}

data Sort = SortOldestFirst
          | SortNewestFirst
          | SortMessageId
          | SortUnsorted
  deriving (Enum)

{-# LINE 69 "src/Notmuch/Binding.chs" #-}

data MessageFlag = MessageFlagMatch
                 | MessageFlagExcluded
                 | MessageFlagGhost
  deriving (Enum)

{-# LINE 70 "src/Notmuch/Binding.chs" #-}

newtype DatabaseHandle = DatabaseHandle (C2HSImp.ForeignPtr (DatabaseHandle))
withDatabaseHandle :: DatabaseHandle -> (C2HSImp.Ptr DatabaseHandle -> IO b) -> IO b
withDatabaseHandle (DatabaseHandle fptr) = C2HSImp.withForeignPtr fptr
{-# LINE 71 "src/Notmuch/Binding.chs" #-}

newtype QueryHandle = QueryHandle (C2HSImp.ForeignPtr (QueryHandle))
withQueryHandle :: QueryHandle -> (C2HSImp.Ptr QueryHandle -> IO b) -> IO b
withQueryHandle (QueryHandle fptr) = C2HSImp.withForeignPtr fptr
{-# LINE 72 "src/Notmuch/Binding.chs" #-}

newtype Threads = Threads (C2HSImp.Ptr (Threads))
{-# LINE 73 "src/Notmuch/Binding.chs" #-}

newtype ThreadHandle = ThreadHandle (C2HSImp.ForeignPtr (ThreadHandle))
withThreadHandle :: ThreadHandle -> (C2HSImp.Ptr ThreadHandle -> IO b) -> IO b
withThreadHandle (ThreadHandle fptr) = C2HSImp.withForeignPtr fptr
{-# LINE 74 "src/Notmuch/Binding.chs" #-}

newtype Messages = Messages (C2HSImp.Ptr (Messages))
{-# LINE 75 "src/Notmuch/Binding.chs" #-}

newtype MessageHandle = MessageHandle (C2HSImp.ForeignPtr (MessageHandle))
withMessageHandle :: MessageHandle -> (C2HSImp.Ptr MessageHandle -> IO b) -> IO b
withMessageHandle (MessageHandle fptr) = C2HSImp.withForeignPtr fptr
{-# LINE 76 "src/Notmuch/Binding.chs" #-}

newtype Tags = Tags (C2HSImp.Ptr (Tags))
{-# LINE 77 "src/Notmuch/Binding.chs" #-}

newtype Directory = Directory (C2HSImp.ForeignPtr (Directory))
withDirectory :: Directory -> (C2HSImp.Ptr Directory -> IO b) -> IO b
withDirectory (Directory fptr) = C2HSImp.withForeignPtr fptr
{-# LINE 78 "src/Notmuch/Binding.chs" #-}

newtype Filenames = Filenames (C2HSImp.ForeignPtr (Filenames))
withFilenames :: Filenames -> (C2HSImp.Ptr Filenames -> IO b) -> IO b
withFilenames (Filenames fptr) = C2HSImp.withForeignPtr fptr
{-# LINE 79 "src/Notmuch/Binding.chs" #-}


deriving instance Storable Threads
deriving instance Storable Messages

instance Show Status where
  show a = System.IO.Unsafe.unsafePerformIO $
    notmuch_status_to_string (fromEnum' a) >>= peekCString

-- | Classy prism for injecting a /libnotmuch/ status code.
class AsNotmuchError s where
  _NotmuchError :: Prism' s Status

instance AsNotmuchError Status where
  _NotmuchError = id

throwOr :: (AsNotmuchError e, MonadError e m) => (a -> m b) -> Either Status a -> m b
throwOr = either (throwError . review _NotmuchError)

-- | Convenience synonym for the promoted 'DatabaseModeReadOnly' constructor.
type RO = 'DatabaseModeReadOnly

-- | Convenience synonym for the promoted 'DatabaseModeReadWrite' constructor.
type RW = 'DatabaseModeReadWrite

-- | A database handle.  The database will be closed and freed when
-- it is garbage collected.
--
-- Use 'query' to perform a search or 'findMessage' to search for a
-- particular message.
--
-- The @Database@ type carries a phantom for the database mode, which
-- is propgated to derived 'Query', 'Thread' and 'Message' objects.
-- This is used to prevent write operations being performed against
-- a read-only database.
--
newtype Database (a :: DatabaseMode) = Database DatabaseHandle

withDatabase :: Database a -> (Ptr DatabaseHandle -> IO b) -> IO b
withDatabase (Database dbh) = withDatabaseHandle dbh

-- | Message object.  Cleaned up when garbage collected.
--
-- The @Message@ type carries a phantom for the database mode, so that
-- write operations are restricted to read/write database sessions.
--
-- There is also a phantom type parameter for the degree of frozenness
-- of the message.  Tag operations on a frozen message are atomic, only
-- becoming visible to other threads/processes after the thaw.  The
-- freeze/thaw behaviour is available via 'withFrozenMessage'.
--
data Message (n :: Nat) (a :: DatabaseMode) = Message
                 ![ForeignPtr () {- owners -}]
  {-# UNPACK #-} !MessageHandle

withMessage :: Message n a -> (Ptr MessageHandle -> IO b) -> IO b
withMessage (Message owners a) k = do
  r <- withMessageHandle a k
  traverse_ touchForeignPtr owners
  pure r

-- | Thread object.  Cleaned up when garbage collected.
--
-- Use 'messages' to get the messages of a thread.
--
-- The @Thread@ type carries a phantom for the database mode, so that
-- write operations on messages derived from it are restricted to
-- read/write database sessions.
--
data Thread (a :: DatabaseMode) = Thread
  {-# UNPACK #-} !(ForeignPtr DatabaseHandle {- owner -})
  {-# UNPACK #-} !(ForeignPtr QueryHandle {- owner -})
  {-# UNPACK #-} !ThreadHandle

withThread :: Thread a -> (Ptr ThreadHandle -> IO b) -> IO b
withThread (Thread dfp qfp a) k = do
  r <- withThreadHandle a k
  touchForeignPtr dfp
  touchForeignPtr qfp
  pure r

-- | Query object.  Cleaned up when garbage collected.
--
-- Use 'messages' or 'threads' to get the results.
--
-- The @Query@ type carries a phantom for the database mode, so that
-- write operations on messages derived from it are restricted to
-- read/write database sessions.
--
data Query (a :: DatabaseMode) = Query
  {-# UNPACK #-} !(ForeignPtr DatabaseHandle)
  {-# UNPACK #-} !QueryHandle

withQuery :: Query a -> (Ptr QueryHandle -> IO b) -> IO b
withQuery (Query owner a) f = do
  r <- withQueryHandle a f
  touchForeignPtr owner
  pure r

fromEnum' :: (Enum a, Integral b) => a -> b
fromEnum' = fromIntegral . fromEnum

-- | If @e == StatusSuccess@ then apply @Right@ in the given effect,
-- otherwise return @pure (Left e)@
--
status :: (Applicative f) => f a -> Status -> f (Either Status a)
status a StatusSuccess = Right <$> a
status _ e = pure $ Left e

toStatus :: (Integral a, Enum b) => a -> b
toStatus = toEnum . fromIntegral


-- | This is an internal class whose instances are the promoted
-- 'DatabaseMode' constructors.
class Mode a where
  getMode :: Proxy a -> DatabaseMode
  upgrade :: (AsNotmuchError e, MonadError e m, MonadIO m) => Database a -> m (Database a)

instance Mode 'DatabaseModeReadOnly where
  getMode _ = DatabaseModeReadOnly
  upgrade = pure

instance Mode 'DatabaseModeReadWrite where
  getMode _ = DatabaseModeReadWrite
  upgrade db =
    liftIO (toEnum . fromIntegral <$> withDatabase db (\dbPtr ->
      notmuch_database_upgrade dbPtr nullFunPtr nullPtr))
    >>= \rv -> case rv of
      StatusSuccess -> pure db
      e -> throwError $ review _NotmuchError e

-- | Open a Notmuch database
--
-- The database will be closed and resources freed when it gets
-- garbage collected.
--
database_open
  :: forall a e m. (AsNotmuchError e, Mode a, MonadError e m, MonadIO m)
  => FilePath
  -> m (Database a)
database_open s =
  liftIO (
    withCString s (\s' -> (fmap . fmap) runIdentity $
      constructF
        Identity
        (fmap (Database . DatabaseHandle) . newForeignPtr notmuch_database_destroy)
        (notmuch_database_open s' (fromEnum' (getMode (Proxy :: Proxy a))))
    ))
  >>= throwOr upgrade

-- notmuch_status_t notmuch_database_compact(path, backup_path, status_cb, closure)

database_get_path :: Database a -> IO FilePath
database_get_path db =
  withDatabase db notmuch_database_get_path >>= peekCString

database_get_version :: Database a -> IO Int
database_get_version db =
  fromIntegral <$> withDatabase db notmuch_database_get_version
{-# LINE 238 "src/Notmuch/Binding.chs" #-}


-- notmuch_database_needs_upgrade ## do automatically for updates
-- notmuch_database_upgrade ## do automatically for updates
-- notmuch_database_begin_atomic ## do automatically for updates
-- notmuch_database_end_atomic ## do automatically for updates

-- notmuch_database_get_directory

-- notmuch_database_add_message

-- notmuch_database_remove_message

database_find_message
  :: (AsNotmuchError e, MonadError e m, MonadIO m)
  => Database a
  -> MessageId
  -> m (Maybe (Message 0 a))
database_find_message =
  database_find_message_x B.useAsCString notmuch_database_find_message
{-# LINE 257 "src/Notmuch/Binding.chs" #-}


database_find_message_by_filename
  :: (AsNotmuchError e, MonadError e m, MonadIO m)
  => Database a -- ^ Database
  -> FilePath   -- ^ Filename
  -> m (Maybe (Message 0 a))
database_find_message_by_filename =
  database_find_message_x withCString notmuch_database_find_message_by_filename
{-# LINE 265 "src/Notmuch/Binding.chs" #-}


database_find_message_x
  :: (AsNotmuchError e, MonadError e m, MonadIO m)
  => (s -> (s' -> IO (Either Status (Maybe (Message 0 a)))) -> IO (Either Status (Maybe (Message 0 a))))
  -> (Ptr DatabaseHandle -> s' -> Ptr (Ptr MessageHandle) -> IO CInt)
  -> Database a -- ^ Database
  -> s
  -> m (Maybe (Message 0 a))
database_find_message_x prep f db@(Database (DatabaseHandle dfp)) s =
  liftIO (withDatabase db $ \db' ->
    prep s $ \s' ->
      constructF
        (\ptr -> if ptr /= nullPtr then Just ptr else Nothing)
        (fmap (Message [castForeignPtr dfp] . MessageHandle)
          . (newForeignPtr notmuch_message_destroy <=< detachPtr))
        (f db' s')
  )
  >>= throwOr pure

-- Tags are always copied from the libnotmuch-managed memory,
-- and all the copying takes place under 'withDatabase', so
-- we don't need to detach the pointer or use a ForeignPtr.
--
-- TODO: check for NULL, indicating error
--
database_get_all_tags :: Database a -> IO [Tag]
database_get_all_tags ptr = withDatabase ptr $ \ptr' ->
  notmuch_database_get_all_tags ptr' >>= tagsToList

-- TODO: check for NULL, indicating error
query_create :: Database a -> String -> IO (Query a)
query_create db@(Database (DatabaseHandle dfp)) s = withCString s $ \s' ->
  withDatabase db $ \db' ->
    notmuch_query_create db' s'
      >>= detachPtr
      >>= fmap (Query dfp . QueryHandle) . newForeignPtr notmuch_query_destroy

query_get_query_string :: Query a -> IO String
query_get_query_string ptr =
  withQuery ptr (notmuch_query_get_query_string >=> peekCString)

query_set_sort :: Query a -> Sort -> IO ()
query_set_sort ptr x = withQuery ptr $ \ptr' ->
  notmuch_query_set_sort ptr' (fromEnum' x)

query_get_sort :: Query a -> IO Sort
query_get_sort ptr = withQuery ptr $
  fmap (toEnum . fromIntegral) . notmuch_query_get_sort
{-# LINE 313 "src/Notmuch/Binding.chs" #-}


query_add_tag_exclude :: Query a -> Tag -> IO ()
query_add_tag_exclude ptr tag = void $
  withQuery ptr $ \ptr' ->
    tagUseAsCString tag $ \s' ->
      notmuch_query_add_tag_exclude ptr' s'

query_search_threads
  :: (AsNotmuchError e, MonadError e m, MonadIO m)
  => Query a -> m [Thread a]
query_search_threads q@(Query dfp (QueryHandle qfp)) =
  liftIO ( withQuery q $ \qPtr ->
    constructF
      Identity
      (threadsToList dfp qfp)
      (notmuch_query_search_threads qPtr)
  )
  >>= throwOr (pure . runIdentity)

query_search_messages
  :: (AsNotmuchError e, MonadError e m, MonadIO m)
  => Query a -> m [Message 0 a]
query_search_messages q@(Query dfp (QueryHandle qfp)) =
  liftIO ( withQuery q $ \qPtr ->
    constructF
      Identity
      (messagesToList [castForeignPtr dfp, castForeignPtr qfp])
      (notmuch_query_search_messages qPtr)
  )
  >>= throwOr (pure . runIdentity)

query_count_x
  :: (AsNotmuchError e, MonadError e m, MonadIO m)
  => (Ptr QueryHandle -> Ptr CUInt -> IO CInt)
  -> Query a -> m Int
query_count_x f q = fmap fromIntegral $
  liftIO (
    withQuery q $ \qPtr ->
      alloca $ \intPtr ->
        toStatus <$> f qPtr intPtr
        >>= status (peek intPtr)
  ) >>= throwOr pure

query_count_messages, query_count_threads
  :: (AsNotmuchError e, MonadError e m, MonadIO m) => Query a -> m Int
query_count_messages = query_count_x notmuch_query_count_messages
{-# LINE 378 "src/Notmuch/Binding.chs" #-}

query_count_threads = query_count_x notmuch_query_count_threads
{-# LINE 379 "src/Notmuch/Binding.chs" #-}



thread_get_thread_id :: Thread a -> IO ThreadId
thread_get_thread_id ptr =
  withThread ptr (notmuch_thread_get_thread_id >=> B.packCString)

thread_get_total_messages :: Thread a -> IO Int
thread_get_total_messages ptr =
  fromIntegral <$> withThread ptr (notmuch_thread_get_total_messages)

thread_get_toplevel_messages :: MonadIO m => Thread a -> m [Message 0 a]
thread_get_toplevel_messages t@(Thread dfp qfp (ThreadHandle tfp))
  = liftIO $ withThread t $ \ptr ->
    notmuch_thread_get_toplevel_messages ptr
    >>= messagesToList [castForeignPtr dfp, castForeignPtr qfp, castForeignPtr tfp]

thread_get_messages :: MonadIO m => Thread a -> m [Message 0 a]
thread_get_messages t@(Thread dfp qfp (ThreadHandle tfp))
  = liftIO $ withThread t $ \ptr ->
    notmuch_thread_get_messages ptr
    >>= messagesToList [castForeignPtr dfp, castForeignPtr qfp, castForeignPtr tfp]

-- notmuch_thread_get_matched_messages -> Int

thread_get_authors :: Thread a -> IO (Maybe B.ByteString)
thread_get_authors ptr = withThread ptr $ \ptr' -> do
  r <- notmuch_thread_get_authors ptr'
  if r == nullPtr
     then pure Nothing
     else Just <$> B.packCString r

thread_get_subject :: Thread a -> IO B.ByteString
thread_get_subject ptr = withThread ptr (notmuch_thread_get_subject >=> B.packCString)
-- notmuch_thread_get_oldest_date
thread_get_newest_date :: Thread a -> IO CLong
thread_get_newest_date = flip withThread notmuch_thread_get_newest_date
{-# LINE 415 "src/Notmuch/Binding.chs" #-}


-- Tags are always copied from the libnotmuch-managed memory,
-- and all the copying takes place under 'withThread', so
-- we don't need to detach the pointer or use a ForeignPtr.
thread_get_tags :: Thread a -> IO [Tag]
thread_get_tags ptr = withThread ptr $
  notmuch_thread_get_tags >=> tagsToList

message_get_message_id :: Message n a -> IO MessageId
message_get_message_id ptr =
  withMessage ptr (notmuch_message_get_message_id >=> B.packCString)

message_get_thread_id :: Message n a -> IO ThreadId
message_get_thread_id ptr =
  withMessage ptr (notmuch_message_get_thread_id >=> B.packCString)

message_get_replies :: MonadIO m => Message n a -> m [Message 0 a]
message_get_replies msg@(Message owners (MessageHandle mfp))
  = liftIO $ withMessage msg $ \ptr ->
    notmuch_message_get_replies ptr
      >>= messagesToList (castForeignPtr mfp : owners)
          -- have to keep this message alive, as well as its owners

message_get_filename :: Message n a -> IO FilePath
message_get_filename ptr =
  withMessage ptr (notmuch_message_get_filename >=> peekCString)

message_get_flag :: Message n a -> MessageFlag -> IO Bool
message_get_flag ptr flag = withMessage ptr $ \ptr' -> do
  (/= 0) <$> notmuch_message_get_flag ptr' (enumToCInt flag)

-- DB NEEDS TO BE WRITABLE???
message_set_flag :: Message n a -> MessageFlag -> Bool -> IO ()
message_set_flag ptr flag v = withMessage ptr $ \ptr' ->
  notmuch_message_set_flag ptr' (enumToCInt flag) (enumToCInt v)

message_get_date :: Message n a -> IO CLong
message_get_date = flip withMessage notmuch_message_get_date
{-# LINE 453 "src/Notmuch/Binding.chs" #-}


-- returns EMPTY STRING on missing header,
-- NOTHING on error (I know, confusing)
--
message_get_header :: Message n a -> B.ByteString -> IO (Maybe B.ByteString)
message_get_header ptr s =
  B.useAsCString s $ \s' ->
    withMessage ptr $ \ptr' -> do
      r <- notmuch_message_get_header ptr' s'
      if r == nullPtr
        then pure Nothing
        else Just <$> B.packCString r

-- Tags are always copied from the libnotmuch-managed memory,
-- and all the copying takes place under 'withMessage', so
-- we don't need to detach the pointer or use a ForeignPtr.
message_get_tags :: Message n a -> IO [Tag]
message_get_tags ptr = withMessage ptr $
  notmuch_message_get_tags >=> tagsToList

-- According to the header file, possible errors are:
--
-- * NOTMUCH_STATUS_READ_ONLY_DATABASE (excluded by @Message n RW@)
--
-- Therefore assume everything worked!
--
message_remove_all_tags :: Message n RW -> IO ()
message_remove_all_tags msg = void $ withMessage msg $
  notmuch_message_remove_all_tags
{-# LINE 482 "src/Notmuch/Binding.chs" #-}


-- According to the header file, possible errors are:
--
-- * NOTMUCH_STATUS_NULL_POINTER (excluded by @Tag@ type)
-- * NOTMUCH_STATUS_TAG_TOO_LONG (excluded by @Tag@ type)
-- * NOTMUCH_STATUS_READ_ONLY_DATABASE (excluded by @Message RW@)
--
-- Therefore assume everything worked!
--
message_add_tag :: Message n RW -> Tag -> IO ()
message_add_tag msg tag = void $ withMessage msg $
  tagUseAsCString tag . notmuch_message_add_tag
{-# LINE 494 "src/Notmuch/Binding.chs" #-}


-- According to the header file, possible errors are:
--
-- * NOTMUCH_STATUS_NULL_POINTER (excluded by @Tag@ type)
-- * NOTMUCH_STATUS_TAG_TOO_LONG (excluded by @Tag@ type)
-- * NOTMUCH_STATUS_READ_ONLY_DATABASE (excluded by @Message RW@)
--
-- Therefore assume everything worked!
--
message_remove_tag :: Message n RW -> Tag -> IO ()
message_remove_tag msg tag = void $ withMessage msg $
  tagUseAsCString tag . notmuch_message_remove_tag
{-# LINE 506 "src/Notmuch/Binding.chs" #-}


-- According to the header file, possible errors are:
--
-- * NOTMUCH_STATUS_READ_ONLY_DATABASE (excluded by @Message n RW@)
--
-- Therefore assume everything worked!
--
message_freeze :: Message n RW -> IO (Message (n + 1) RW)
message_freeze msg = withMessage msg notmuch_message_freeze $> coerce msg

-- According to the header file, possible errors are:
--
-- * NOTMUCH_STATUS_READ_ONLY_DATABASE (excluded by @Message n RW@)
-- * NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW
--     (excluded by @(CmpNat n 0 ~ 'GT) => Message n RW@)
--
-- Therefore assume everything worked!
--
message_thaw :: (1 <= n) => Message n RW -> IO (Message (n - 1) RW)
message_thaw msg = withMessage msg notmuch_message_thaw $> coerce msg

-- message_maildir_flags_to_tags
-- message_tags_to_maildir_flags

-- directory functions

-- filenames functions


--
-- UTILITY FUNCTIONS
--

enumToCInt :: Enum a => a -> CInt
enumToCInt = fromIntegral . fromEnum

constructF
  :: (Storable ptr, Traversable t)
  => (ptr -> t ptr)
  -- ^ Inspect received pointer and lift it into a Traversable
  -> (ptr -> IO r)
  -- ^ Wrap pointer, including attaching finalizers
  -> (Ptr ptr -> IO CInt)
  -- ^ C double-pointer-style constructor
  -> IO (Either Status (t r))
constructF mkF dcon constructor =
  alloca $ \ptr ->
    toEnum . fromIntegral <$> constructor ptr
    >>= status (traverse dcon . mkF =<< peek ptr)


type PtrToList =
  forall a b ptr.
     (ptr -> IO CInt) -- ^ Iterator predicate function
  -> (ptr -> IO a)    -- ^ Iterator get function
  -> (ptr -> IO ())   -- ^ Iterator next function
  -> (a -> IO b)      -- ^ Item mapper
  -> ptr              -- ^ Iterator
  -> IO [b]

-- | Strictly read a C iterator into a list.
--
ptrToList :: PtrToList
ptrToList = ptrToListIO id

-- | Lazily read a C iterator into a list.
--
lazyPtrToList :: PtrToList
lazyPtrToList = ptrToListIO System.IO.Unsafe.unsafeInterleaveIO

ptrToListIO :: (forall r. IO r -> IO r) -> PtrToList
ptrToListIO tweakIO test get next f ptr = go
  where
  go = test ptr >>= \valid -> case valid of
    0 -> pure []
    _ -> (:)
          <$> (get ptr >>= f >>= \x -> next ptr $> x)
          <*> tweakIO go

-- It's assumed that tag lists are short and that it doesn't make
-- sense to read the list lazily.  Tere
tagsToList :: Tags -> IO [Tag]
tagsToList = ptrToList
  notmuch_tags_valid
{-# LINE 590 "src/Notmuch/Binding.chs" #-}

  notmuch_tags_get
{-# LINE 591 "src/Notmuch/Binding.chs" #-}

  notmuch_tags_move_to_next
{-# LINE 592 "src/Notmuch/Binding.chs" #-}

  tagFromCString

threadsToList
  :: ForeignPtr DatabaseHandle
  -> ForeignPtr QueryHandle
  -> Threads
  -> IO [Thread mode]
threadsToList dfp qfp = lazyPtrToList
  notmuch_threads_valid
{-# LINE 601 "src/Notmuch/Binding.chs" #-}

  notmuch_threads_get
{-# LINE 602 "src/Notmuch/Binding.chs" #-}

  notmuch_threads_move_to_next
{-# LINE 603 "src/Notmuch/Binding.chs" #-}

  (fmap (Thread dfp qfp . ThreadHandle) . newForeignPtr notmuch_thread_destroy <=< detachPtr)

messagesToList :: [ForeignPtr ()] -> Messages -> IO [Message 0 mode]
messagesToList owners = lazyPtrToList
  notmuch_messages_valid
{-# LINE 608 "src/Notmuch/Binding.chs" #-}

  notmuch_messages_get
{-# LINE 609 "src/Notmuch/Binding.chs" #-}

  notmuch_messages_move_to_next
{-# LINE 610 "src/Notmuch/Binding.chs" #-}

  (fmap (Message owners . MessageHandle) . newForeignPtr notmuch_message_destroy <=< detachPtr)

foreign import ccall "Notmuch/Binding.chs.h &notmuch_database_destroy"
  notmuch_database_destroy :: C2HSImp.FinalizerPtr DatabaseHandle

foreign import ccall "Notmuch/Binding.chs.h &notmuch_query_destroy"
  notmuch_query_destroy :: C2HSImp.FinalizerPtr QueryHandle

foreign import ccall "Notmuch/Binding.chs.h &notmuch_thread_destroy"
  notmuch_thread_destroy :: C2HSImp.FinalizerPtr ThreadHandle

foreign import ccall "Notmuch/Binding.chs.h &notmuch_message_destroy"
  notmuch_message_destroy :: C2HSImp.FinalizerPtr MessageHandle

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_status_to_string"
  notmuch_status_to_string :: (C2HSImp.CInt -> (IO (C2HSImp.Ptr C2HSImp.CChar)))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_database_upgrade"
  notmuch_database_upgrade :: ((C2HSImp.Ptr (DatabaseHandle)) -> ((C2HSImp.FunPtr ((C2HSImp.Ptr ()) -> (C2HSImp.CDouble -> (IO ())))) -> ((C2HSImp.Ptr ()) -> (IO C2HSImp.CInt))))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_database_open"
  notmuch_database_open :: ((C2HSImp.Ptr C2HSImp.CChar) -> (C2HSImp.CInt -> ((C2HSImp.Ptr (C2HSImp.Ptr (DatabaseHandle))) -> (IO C2HSImp.CInt))))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_database_get_path"
  notmuch_database_get_path :: ((C2HSImp.Ptr (DatabaseHandle)) -> (IO (C2HSImp.Ptr C2HSImp.CChar)))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_database_get_version"
  notmuch_database_get_version :: ((C2HSImp.Ptr (DatabaseHandle)) -> (IO C2HSImp.CUInt))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_database_find_message"
  notmuch_database_find_message :: ((C2HSImp.Ptr (DatabaseHandle)) -> ((C2HSImp.Ptr C2HSImp.CChar) -> ((C2HSImp.Ptr (C2HSImp.Ptr (MessageHandle))) -> (IO C2HSImp.CInt))))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_database_find_message_by_filename"
  notmuch_database_find_message_by_filename :: ((C2HSImp.Ptr (DatabaseHandle)) -> ((C2HSImp.Ptr C2HSImp.CChar) -> ((C2HSImp.Ptr (C2HSImp.Ptr (MessageHandle))) -> (IO C2HSImp.CInt))))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_database_get_all_tags"
  notmuch_database_get_all_tags :: ((C2HSImp.Ptr (DatabaseHandle)) -> (IO (Tags)))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_query_create"
  notmuch_query_create :: ((C2HSImp.Ptr (DatabaseHandle)) -> ((C2HSImp.Ptr C2HSImp.CChar) -> (IO (C2HSImp.Ptr (QueryHandle)))))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_query_get_query_string"
  notmuch_query_get_query_string :: ((C2HSImp.Ptr (QueryHandle)) -> (IO (C2HSImp.Ptr C2HSImp.CChar)))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_query_set_sort"
  notmuch_query_set_sort :: ((C2HSImp.Ptr (QueryHandle)) -> (C2HSImp.CInt -> (IO ())))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_query_get_sort"
  notmuch_query_get_sort :: ((C2HSImp.Ptr (QueryHandle)) -> (IO C2HSImp.CInt))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_query_add_tag_exclude"
  notmuch_query_add_tag_exclude :: ((C2HSImp.Ptr (QueryHandle)) -> ((C2HSImp.Ptr C2HSImp.CChar) -> (IO C2HSImp.CInt)))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_query_search_threads"
  notmuch_query_search_threads :: ((C2HSImp.Ptr (QueryHandle)) -> ((C2HSImp.Ptr (Threads)) -> (IO C2HSImp.CInt)))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_query_search_messages"
  notmuch_query_search_messages :: ((C2HSImp.Ptr (QueryHandle)) -> ((C2HSImp.Ptr (Messages)) -> (IO C2HSImp.CInt)))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_query_count_messages"
  notmuch_query_count_messages :: ((C2HSImp.Ptr (QueryHandle)) -> ((C2HSImp.Ptr C2HSImp.CUInt) -> (IO C2HSImp.CInt)))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_query_count_threads"
  notmuch_query_count_threads :: ((C2HSImp.Ptr (QueryHandle)) -> ((C2HSImp.Ptr C2HSImp.CUInt) -> (IO C2HSImp.CInt)))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_thread_get_thread_id"
  notmuch_thread_get_thread_id :: ((C2HSImp.Ptr (ThreadHandle)) -> (IO (C2HSImp.Ptr C2HSImp.CChar)))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_thread_get_total_messages"
  notmuch_thread_get_total_messages :: ((C2HSImp.Ptr (ThreadHandle)) -> (IO C2HSImp.CInt))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_thread_get_toplevel_messages"
  notmuch_thread_get_toplevel_messages :: ((C2HSImp.Ptr (ThreadHandle)) -> (IO (Messages)))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_thread_get_messages"
  notmuch_thread_get_messages :: ((C2HSImp.Ptr (ThreadHandle)) -> (IO (Messages)))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_thread_get_authors"
  notmuch_thread_get_authors :: ((C2HSImp.Ptr (ThreadHandle)) -> (IO (C2HSImp.Ptr C2HSImp.CChar)))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_thread_get_subject"
  notmuch_thread_get_subject :: ((C2HSImp.Ptr (ThreadHandle)) -> (IO (C2HSImp.Ptr C2HSImp.CChar)))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_thread_get_newest_date"
  notmuch_thread_get_newest_date :: ((C2HSImp.Ptr (ThreadHandle)) -> (IO C2HSImp.CLong))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_thread_get_tags"
  notmuch_thread_get_tags :: ((C2HSImp.Ptr (ThreadHandle)) -> (IO (Tags)))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_message_get_message_id"
  notmuch_message_get_message_id :: ((C2HSImp.Ptr (MessageHandle)) -> (IO (C2HSImp.Ptr C2HSImp.CChar)))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_message_get_thread_id"
  notmuch_message_get_thread_id :: ((C2HSImp.Ptr (MessageHandle)) -> (IO (C2HSImp.Ptr C2HSImp.CChar)))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_message_get_replies"
  notmuch_message_get_replies :: ((C2HSImp.Ptr (MessageHandle)) -> (IO (Messages)))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_message_get_filename"
  notmuch_message_get_filename :: ((C2HSImp.Ptr (MessageHandle)) -> (IO (C2HSImp.Ptr C2HSImp.CChar)))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_message_get_flag"
  notmuch_message_get_flag :: ((C2HSImp.Ptr (MessageHandle)) -> (C2HSImp.CInt -> (IO C2HSImp.CInt)))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_message_set_flag"
  notmuch_message_set_flag :: ((C2HSImp.Ptr (MessageHandle)) -> (C2HSImp.CInt -> (C2HSImp.CInt -> (IO ()))))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_message_get_date"
  notmuch_message_get_date :: ((C2HSImp.Ptr (MessageHandle)) -> (IO C2HSImp.CLong))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_message_get_header"
  notmuch_message_get_header :: ((C2HSImp.Ptr (MessageHandle)) -> ((C2HSImp.Ptr C2HSImp.CChar) -> (IO (C2HSImp.Ptr C2HSImp.CChar))))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_message_get_tags"
  notmuch_message_get_tags :: ((C2HSImp.Ptr (MessageHandle)) -> (IO (Tags)))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_message_remove_all_tags"
  notmuch_message_remove_all_tags :: ((C2HSImp.Ptr (MessageHandle)) -> (IO C2HSImp.CInt))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_message_add_tag"
  notmuch_message_add_tag :: ((C2HSImp.Ptr (MessageHandle)) -> ((C2HSImp.Ptr C2HSImp.CChar) -> (IO C2HSImp.CInt)))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_message_remove_tag"
  notmuch_message_remove_tag :: ((C2HSImp.Ptr (MessageHandle)) -> ((C2HSImp.Ptr C2HSImp.CChar) -> (IO C2HSImp.CInt)))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_message_freeze"
  notmuch_message_freeze :: ((C2HSImp.Ptr (MessageHandle)) -> (IO C2HSImp.CInt))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_message_thaw"
  notmuch_message_thaw :: ((C2HSImp.Ptr (MessageHandle)) -> (IO C2HSImp.CInt))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_tags_valid"
  notmuch_tags_valid :: ((Tags) -> (IO C2HSImp.CInt))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_tags_get"
  notmuch_tags_get :: ((Tags) -> (IO (C2HSImp.Ptr C2HSImp.CChar)))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_tags_move_to_next"
  notmuch_tags_move_to_next :: ((Tags) -> (IO ()))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_threads_valid"
  notmuch_threads_valid :: ((Threads) -> (IO C2HSImp.CInt))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_threads_get"
  notmuch_threads_get :: ((Threads) -> (IO (C2HSImp.Ptr (ThreadHandle))))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_threads_move_to_next"
  notmuch_threads_move_to_next :: ((Threads) -> (IO ()))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_messages_valid"
  notmuch_messages_valid :: ((Messages) -> (IO C2HSImp.CInt))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_messages_get"
  notmuch_messages_get :: ((Messages) -> (IO (C2HSImp.Ptr (MessageHandle))))

foreign import ccall unsafe "Notmuch/Binding.chs.h notmuch_messages_move_to_next"
  notmuch_messages_move_to_next :: ((Messages) -> (IO ()))