{-# LANGUAGE DeriveDataTypeable     #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE ScopedTypeVariables    #-}
module Database.EventStore.Internal.Subscription.Api where
import Database.EventStore.Internal.Callback
import Database.EventStore.Internal.Communication
import Database.EventStore.Internal.Control
import Database.EventStore.Internal.Types
import Database.EventStore.Internal.Prelude
import Database.EventStore.Internal.Stream
import Database.EventStore.Internal.Subscription.Packages
import Database.EventStore.Internal.Subscription.Types
submit :: Callback SubAction -> ResolvedEvent -> IO ()
submit s xs = fulfill s (Submit xs)
dropped :: Callback SubAction -> SubDropReason -> IO ()
dropped s r = fulfill s (Dropped r)
confirmed :: Callback SubAction -> SubDetails -> IO ()
confirmed s d = fulfill s (Confirmed d)
class Subscription s where
  
  
  nextEventMaybeSTM :: s -> STM (Maybe ResolvedEvent)
  
  getSubscriptionDetailsSTM :: s -> STM SubDetails
  
  unsubscribe :: s -> IO ()
class SubscriptionStream s t | t -> s where
    subscriptionStream :: s -> StreamId t
nextEvent :: Subscription s => s -> IO ResolvedEvent
nextEvent s = atomically $ do
  outcome <- nextEventMaybeSTM s
  case outcome of
    Just e  -> return e
    Nothing -> retrySTM
nextEventMaybe :: Subscription s => s -> IO (Maybe ResolvedEvent)
nextEventMaybe = atomically . nextEventMaybeSTM
waitConfirmation :: Subscription s => s -> IO ()
waitConfirmation s = atomically $ do
    _ <- getSubscriptionDetailsSTM s
    return ()
unsubscribeConfirmedSTM :: Subscription s => s -> STM Bool
unsubscribeConfirmedSTM s = do
  let action = do
        _ <- getSubscriptionDetailsSTM s
        return False
  catchSTM action $ \(_ :: SomeException) -> return True
unsubscribeConfirmed :: Subscription s => s -> IO Bool
unsubscribeConfirmed = atomically . unsubscribeConfirmedSTM
waitUnsubscribeConfirmed :: Subscription s => s -> IO ()
waitUnsubscribeConfirmed s = atomically $
    unlessM (unsubscribeConfirmedSTM s) retrySTM
subUnsubscribe :: (Pub pub, Subscription s) => pub -> s -> IO ()
subUnsubscribe pub s = do
  outcome <- atomically $ do
    unsubscribed <- unsubscribeConfirmedSTM s
    if unsubscribed
      then return Nothing
      else Just <$> getSubscriptionDetailsSTM s
  for_ outcome $ \details -> do
    let pkg = createUnsubscribePackage (subId details)
    publishWith pub (SendPackage pkg)
getSubscriptionId :: Subscription s => s -> IO SubscriptionId
getSubscriptionId s = atomically $ do
  details <- getSubscriptionDetailsSTM s
  return (SubscriptionId $ subId details)