{-# LANGUAGE OverloadedStrings  #-}

-- | Provides a higher level interface to the rest functions.
--   Preperly writes to the rate-limit loop. Creates separate
--   MVars for each call
module Discord.Internal.Rest
  ( module Discord.Internal.Types
  , DiscordHandleRestChan
  , Request(..)
  , writeRestCall
  , startRestThread
  , RestCallInternalException(..)
  ) where

import Prelude hiding (log)
import Data.Aeson (FromJSON, eitherDecode)
import Control.Concurrent.Chan
import Control.Concurrent.MVar
import Control.Concurrent (forkIO, ThreadId)
import qualified Data.ByteString.Lazy as BL
import qualified Data.Text as T


import Discord.Internal.Types
import Discord.Internal.Rest.HTTP

type DiscordHandleRestChan = Chan (String, JsonRequest, MVar (Either RestCallInternalException BL.ByteString))

-- | Starts the http request thread. Please only call this once
startRestThread :: Auth -> Chan T.Text -> IO (DiscordHandleRestChan, ThreadId)
startRestThread :: Auth -> Chan Text -> IO (DiscordHandleRestChan, ThreadId)
startRestThread Auth
auth Chan Text
log = do
  DiscordHandleRestChan
c <- IO DiscordHandleRestChan
forall a. IO (Chan a)
newChan
  ThreadId
tid <- IO () -> IO ThreadId
forkIO (IO () -> IO ThreadId) -> IO () -> IO ThreadId
forall a b. (a -> b) -> a -> b
$ Auth -> DiscordHandleRestChan -> Chan Text -> IO ()
restLoop Auth
auth DiscordHandleRestChan
c Chan Text
log
  (DiscordHandleRestChan, ThreadId)
-> IO (DiscordHandleRestChan, ThreadId)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (DiscordHandleRestChan
c, ThreadId
tid)

-- | Execute a request blocking until a response is received
writeRestCall :: (Request (r a), FromJSON a) => DiscordHandleRestChan -> r a -> IO (Either RestCallInternalException a)
writeRestCall :: DiscordHandleRestChan
-> r a -> IO (Either RestCallInternalException a)
writeRestCall DiscordHandleRestChan
c r a
req = do
  MVar (Either RestCallInternalException ByteString)
m <- IO (MVar (Either RestCallInternalException ByteString))
forall a. IO (MVar a)
newEmptyMVar
  DiscordHandleRestChan
-> (String, JsonRequest,
    MVar (Either RestCallInternalException ByteString))
-> IO ()
forall a. Chan a -> a -> IO ()
writeChan DiscordHandleRestChan
c (r a -> String
forall a. Request a => a -> String
majorRoute r a
req, r a -> JsonRequest
forall a. Request a => a -> JsonRequest
jsonRequest r a
req, MVar (Either RestCallInternalException ByteString)
m)
  Either RestCallInternalException ByteString
r <- MVar (Either RestCallInternalException ByteString)
-> IO (Either RestCallInternalException ByteString)
forall a. MVar a -> IO a
readMVar MVar (Either RestCallInternalException ByteString)
m
  Either RestCallInternalException a
-> IO (Either RestCallInternalException a)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either RestCallInternalException a
 -> IO (Either RestCallInternalException a))
-> Either RestCallInternalException a
-> IO (Either RestCallInternalException a)
forall a b. (a -> b) -> a -> b
$ case ByteString -> Either String a
forall a. FromJSON a => ByteString -> Either String a
eitherDecode (ByteString -> Either String a)
-> Either RestCallInternalException ByteString
-> Either RestCallInternalException (Either String a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Either RestCallInternalException ByteString
r of
    Right (Right a
o) -> a -> Either RestCallInternalException a
forall a b. b -> Either a b
Right a
o
    Right (Left String
er) -> RestCallInternalException -> Either RestCallInternalException a
forall a b. a -> Either a b
Left (String -> ByteString -> RestCallInternalException
RestCallInternalNoParse String
er (case Either RestCallInternalException ByteString
r of Right ByteString
x -> ByteString
x
                                                                   Left RestCallInternalException
_ -> ByteString
""))
    Left RestCallInternalException
e -> RestCallInternalException -> Either RestCallInternalException a
forall a b. a -> Either a b
Left RestCallInternalException
e