-- | The Console process has two main purposes. It is a telnet-like
--   interface with the user and it is our first simple logging device
--   for what happens inside the system.
{-# LANGUAGE ScopedTypeVariables #-}
module Process.Console
    ( start
    )
where

import Control.Concurrent
import Control.Concurrent.STM
import Control.Monad.Reader

import Prelude hiding (catch)


import Process
import qualified Process.Status as St
import Supervisor


data Cmd = Quit -- ^ Quit the program
         | Show -- ^ Show current state
         | Help -- ^ Print Help message
         | Unknown String -- ^ Unknown command
         deriving (Eq, Show)

type CmdChannel = TChan Cmd

data CF = CF { cmdCh :: CmdChannel
             , wrtCh :: TChan String }

instance Logging CF where
    logName _ = "Process.Console"

-- | Start the logging process and return a channel to it. Sending on this
--   Channel means writing stuff out on stdOut
start :: TMVar () -> St.StatusChannel -> SupervisorChannel -> IO ThreadId
start waitC statusC supC = do
    cmdC <- readerP -- We shouldn't be doing this in the long run
    wrtC <- writerP
    spawnP (CF cmdC wrtC) () ({-# SCC "Console" #-}
                                catchP eventLoop (defaultStopHandler supC))
  where
    eventLoop = do
        c <- asks cmdCh
        o <- asks wrtCh
        m <- liftIO . atomically $ readTChan c
        case m of
            Quit -> liftIO . atomically $ putTMVar waitC ()
            Help -> liftIO . atomically $ writeTChan o helpMessage
            (Unknown n) -> liftIO . atomically $ writeTChan o $ "Uknown command: " ++ n
            Show -> do
                v <- liftIO newEmptyTMVarIO
                liftIO . atomically $ writeTChan statusC (St.RequestAllTorrents v)
                sts <- liftIO . atomically $ takeTMVar v
                liftIO . atomically $ writeTChan o (show sts)
        eventLoop

helpMessage :: String
helpMessage = concat
    [ "Command Help:\n"
    , "\n"
    , "  help    - Show this help\n"
    , "  quit    - Quit the program\n"
    , "  show    - Show the current downloading status\n"
    ]

writerP :: IO (TChan String)
writerP = do wrt <- newTChanIO
             _ <- forkIO $ lp wrt
             return wrt
  where lp wCh = {-# SCC "writerP" #-}
                    forever (do m <- atomically $ readTChan wCh
                                putStrLn m)

readerP :: IO CmdChannel
readerP = do cmd <- newTChanIO
             _ <- forkIO $ lp cmd
             return cmd
  where lp cmd = {-# SCC "readerP" #-} forever $
           do l <- getLine
              atomically $ writeTChan cmd
                (case l of
                   "help" -> Help
                   "quit" -> Quit
                   "show" -> Show
                   c      -> Unknown c)

