-- {-# LANGUAGE OverloadedStrings #-}

-- | Simple wrapper on top of simpleBot
-- adding applicative parser
-- and `runBotWithParts` shorthand.
--
-- `runBotWithParts` allows passing
-- initialization function that inits
-- all bot parts and returns them as list.

module Network.IRC.Bot.Run (
    runBotWithParts
  ) where

import Data.ByteString            (ByteString)
import Data.Set                   (Set)
import Options.Applicative        (Parser)

import qualified Control.Concurrent
import qualified Control.Monad
import qualified Data.List
import qualified System.IO

import Network.IRC.Bot.BotMonad   (BotMonad(..), BotPartT)
import Network.IRC.Bot.Core       (BotConf(..), simpleBot)
import Network.IRC.Bot.Options    (execBotOptsParser)
import Network.IRC.Bot.Part.Channels (initChannelsPart)
import Network.IRC.Bot.Part.Ping     (pingPart)


-- | Run bot with user provided initialization
-- function returning bot parts.
runBotWithParts :: IO [BotPartT IO ()] -> IO ()
runBotWithParts :: IO [BotPartT IO ()] -> IO ()
runBotWithParts IO [BotPartT IO ()]
parts = forall extra.
Parser extra -> (extra -> IO [BotPartT IO ()]) -> IO ()
runBotWithParts' (forall (f :: * -> *) a. Applicative f => a -> f a
pure ()) (forall a b. a -> b -> a
const IO [BotPartT IO ()]
parts)

-- | Run bot with user provided initialization
-- function returning bot parts.
--
-- Accepts another `optparse-applicative` `Parser` for extending
-- built-in one.
runBotWithParts' :: Parser extra
                -> (extra -> IO [BotPartT IO ()])
                -> IO ()
runBotWithParts' :: forall extra.
Parser extra -> (extra -> IO [BotPartT IO ()]) -> IO ()
runBotWithParts' Parser extra
extrasParser extra -> IO [BotPartT IO ()]
initUserParts = do
  (BotConf
botOptions, extra
extras) <- forall extra. Parser extra -> IO (BotConf, extra)
execBotOptsParser Parser extra
extrasParser

  [BotPartT IO ()]
ircParts <- forall (m :: * -> *).
BotMonad m =>
IO [m ()] -> Set ByteString -> IO [m ()]
initParts (extra -> IO [BotPartT IO ()]
initUserParts extra
extras) (BotConf -> Set ByteString
channels BotConf
botOptions)
  ([ThreadId]
tids, IO ()
reconnect) <- BotConf -> [BotPartT IO ()] -> IO ([ThreadId], IO ())
simpleBot BotConf
botOptions [BotPartT IO ()]
ircParts
  Bool
hasStdin <- IO Bool
System.IO.isEOF
  case Bool
hasStdin of
    Bool
True -> forall (f :: * -> *) a b. Applicative f => f a -> f b
Control.Monad.forever forall a b. (a -> b) -> a -> b
$ Int -> IO ()
Control.Concurrent.threadDelay Int
1000000000
    Bool
False -> do
      let loop :: IO ()
loop = do
            String
l <- IO String
getLine
            forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
Control.Monad.unless (String
"quit" forall a. Eq a => [a] -> [a] -> Bool
`Data.List.isPrefixOf` String
l) forall a b. (a -> b) -> a -> b
$ do
              IO ()
reconnect
              IO ()
loop

      IO ()
loop
  forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ ThreadId -> IO ()
Control.Concurrent.killThread [ThreadId]
tids

-- Init channels part and all user parts
initParts :: (BotMonad m)
          => (IO [m ()])     -- ^ User provided parts
          -> Set ByteString  -- ^ Set of channels to join
          -> IO [m ()]
initParts :: forall (m :: * -> *).
BotMonad m =>
IO [m ()] -> Set ByteString -> IO [m ()]
initParts IO [m ()]
initUser Set ByteString
chans = do
  (TVar (Set ByteString)
_, m ()
channelsPart) <- forall (m :: * -> *).
BotMonad m =>
Set ByteString -> IO (TVar (Set ByteString), m ())
initChannelsPart Set ByteString
chans
  [m ()]
userParts <- IO [m ()]
initUser
  forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ m ()
channelsPartforall a. a -> [a] -> [a]
:forall (m :: * -> *). BotMonad m => m ()
pingPartforall a. a -> [a] -> [a]
:[m ()]
userParts