-- |
-- Module : Network.MicrosoftAzure.ServiceBus.Topic
-- Description : API for reading from and writing to ServiceBus Topic
-- Copyright : (c) Hemanth Kapila, 2014
-- License : BSD3
-- Maintainer : saihemanth@gmail.com
-- Stability  : Experimental
--
-- Provides API to pull from and push to ServiceBus topic
-- Please refer to <http://msdn.microsoft.com/en-us/library/hh780752.aspx Service Bus Rest API> for information on the API provided by
-- Microsoft Service bus.
--
-- Simple example for how to use this library is as below
--
-- @
-- import Network.MicrosoftAzure.ServiceBus.Topic
-- import Network.MicrosoftAzure.ServiceBus
-- import qualified Data.ByteString.Char8 as C
--
-- topicName = "topicName"
-- subscriptionName = "subscriptionName"
-- sbNamespace = "namespace"
-- sbIssuerKey = C.pack "1287361251262as="
-- sbIssuerName = C.pack "owner"
--
-- sbinfo = SBInfo sbNamespace sbIssuerName sbIssuerKey
-- message = C.pack "Hello from Haskell"
--
-- main = do
--   sbContext <- sbContext sbinfo
--   sendTopicBS topicName message sbContext
--   res <- destructiveRead topicName subscriptionName 30 sbContext
--   print res
-- @
--
-- see examples (available as a part of distribution) for a more detailed example.

module Network.MicrosoftAzure.ServiceBus.Topic(
  -- * Pushing data to Topic
  sendTopicBS,
  sendTopicLBS,
  sendTopicBodySrc,
  -- * Reading data from Topic
  destructiveRead,
  peekLockTopic
   )where

import qualified Data.ByteString.Char8 as C
import qualified Data.ByteString.Lazy as L
import Data.Conduit
import Data.Int

import Network.MicrosoftAzure.ACS
import Network.MicrosoftAzure.ServiceBus.SBTypes
import qualified Data.ByteString.Char8 as C
import qualified Data.ByteString.Lazy as L
import Network.HTTP.Conduit hiding (requestBodySource)
import Network.HTTP.Client.Conduit hiding (httpLbs)
import Network.HTTP.Types.Method (methodDelete, methodPost,methodPut)
import Network.HTTP.Types.Header
import Network.HTTP.Types.Method

import qualified Data.CaseInsensitive as CI
import Network.Connection (TLSSettings (..))
import Data.Aeson
import Network(withSocketsDo)

-- | Internal low-level method for performing HTTP calls.
--
-- Not exposed to the user
sendTopicRequest :: String -> RequestBody -> SBContext -> IO ()
sendTopicRequest topicName body (SBContext baseUrl manager aContext)  = do
  token <- acsToken manager aContext
  reqInit <- parseUrl (baseUrl ++ "/" ++ topicName ++ "/messages")
  withSocketsDo $ httpLbs (reqInit { method = methodPost,
                     requestHeaders = [token],
                     requestBody = body
                   }) manager
  return ()

-- | Internal low-level method for creating the HTTP calls. For internal use.
--
-- Not exposed to the user
destructiveReadRequest :: String -> String -> Int -> SBContext -> IO L.ByteString
destructiveReadRequest topic subsc timeout (SBContext baseUrl manager aContext) = do
  token <- acsToken manager aContext
  reqInit <- parseUrl (baseUrl ++ "/" ++ topic ++ "/Subscriptions/" ++ subsc ++ "/messages/head?timeout=" ++ (show timeout))
  res <-withSocketsDo $  httpLbs ( reqInit { method = methodDelete,
                     requestHeaders = [token]
                   }) manager
  return $ responseBody res

-- | publish a message containing 'C.ByteString' to a topic.
--
-- The following publishes a strict bytestring \bs\ to topic \t\
--
-- @
-- sendTopicBS t bs ctx
-- @
sendTopicBS ::  String -> C.ByteString -> SBContext -> IO ()
sendTopicBS   topicName content context =
  sendTopicRequest  topicName (RequestBodyBS content) context


-- | publish a message containing 'L.ByteString' to a topic
--
--  The following publishes a lazy bytestring ,\lbs\, to topic \t\,
--
-- @
-- sendTopicLBS t lbs  ctx
-- @
--
sendTopicLBS :: String -> L.ByteString -> SBContext -> IO ()
sendTopicLBS  topicName content context =
  sendTopicRequest  topicName (RequestBodyLBS content) context


-- | publish from a 'Source' (refer to 'requestBodySource')
sendTopicBodySrc ::  String -> Int64 -> Source IO C.ByteString -> SBContext -> IO ()
sendTopicBodySrc  topicName  len bodysrc context =   sendTopicRequest topicName (requestBodySource len bodysrc) context


-- | Reads and deletes the message from a topic at a given subscription.
--
--
-- In order to destructively read the latest message from the subscription /subsc/ on topic /t/  (with a time out of n seconds),
--
--
-- @
-- destructiveRead t subsc n context
-- @
--  Note that the timeout can be at the most 55 seconds.
destructiveRead :: String -> String -> Int -> SBContext -> IO (L.ByteString)
destructiveRead  topic subsc timeout context =  destructiveReadRequest  topic subsc (timeout `mod` 55) context

-- | Peek Lock Message from a Topic. Non-Destructive Read.
--
-- Atomically retrieves the message from a topic (on a given subscription)  without deleting it. The message is locked for a duration so that it is not visible to other
-- receivers.
--
-- Refer <http://msdn.microsoft.com/en-us/library/hh780722.aspx ServiceBus documentation> for semantics of the underlying REST API.
peekLockTopic :: String -> String -> Int -> SBContext -> IO (LockedMsgInfo,L.ByteString)
peekLockTopic topic subscr timeout (SBContext baseUrl manager aContext) = do
    token <- acsToken manager aContext
    reqInit <- parseUrl (baseUrl ++ "/" ++ topic ++ "/Subscriptions/" ++ subscr ++ "/messages/head?timeout=" ++ (show timeout))
    res <-withSocketsDo $  httpLbs (reqInit { method = methodPost,
                              requestHeaders = [token]
                            }) manager
    return $ (getQLI res,responseBody res)