--{-#LANGUAGE GeneralizedNewtypeDeriving#-}
module IRC.Bot.BotData
    (BotData(..)
    ,BotDataIO
    ,BotDataV
    ,BotDataVIO
    ,writeMessage
    ) where

import Control.Monad.Reader (runReaderT, ask, liftIO)
import Network
import System.IO
import Control.Monad.Reader
import Text.Printf
import Data.Maybe
import Text.Regex
import qualified Data.Text as T
import qualified IRC.Data.Command as ID
import qualified IRC.Data.ServerClientMessage as ID
import qualified IRC.Agent as IA
import qualified IRC.Bot.ChannelUser as IBCU
import qualified Control.Concurrent.STM.TVar as TV
import qualified Control.Concurrent.STM as TV

-- # Basic Idea around IRC Bot
-- The IRC Bot should make a connection to the server with the given
-- information. The Bot should then start reading in input from the server. The
-- Server will give instructions to the Bot if there are any errors. After
-- performing those instructions, the bot will start listening for instructions
-- given from chat. Each instruction should be parsed. Parsing possibly should
-- be performed before forking, but could be after. The Forked thread will
-- perform a number of operations, after which the thread will end. The Main
-- read loop must not be blocked.
data BotData = BotData 
    { agent      :: IA.Agent
    , messages   :: [ID.UserMsg] 
    , users      :: [IBCU.ChannelUser]
    , actions    :: [Action]
    }

type BotDataIO = ReaderT BotData IO

type BotDataV = TV.TVar BotData

type BotDataVIO = ReaderT BotDataV IO 


-- Write the UserMsg to the Log
writeMessage :: ID.UserMsg -> BotDataVIO ()
writeMessage msg = do
    dv <- ask
    d <- liftIO $ TV.readTVarIO dv
    liftIO $ TV.atomically $ TV.writeTVar dv 
       (d { messages = ( msg:(messages d)) })

data Action = Action 
    { group :: IBCU.Group -- Security Level
    , regexAsString :: T.Text-- What causes it
    , compiledRegex :: Regex
    , action :: BotDataVIO ()
    }


-- User is authorized if it his group is less than what it required for 
-- usage.
userIsAuthorized :: IBCU.ChannelUser -> Action -> Bool
userIsAuthorized cu a = (group a) >= (IBCU.group cu) 


-- Creates the action. It compiles the supplied expression ( supplied as 
-- Text) to regex. 
createAction :: IBCU.Group -> T.Text -> BotDataVIO() -> Action
createAction g r b = Action 
    { group = g
    , regexAsString = r
    , compiledRegex = mkRegex $ T.unpack r
    , action = b
    }

-- Runs regex matching against the Text supplied and performs the action.
-- It does not check whether the user has authorization to perform the 
-- action.
runAction :: T.Text -> Action -> BotDataVIO Bool
runAction txt a = do
    let str =  T.unpack txt
    case (matchRegex (compiledRegex a) str) of
        Nothing -> return False
        Just _ -> do
            action a
            return True
