module Network.SimpleServer.Examples.ChatClient(main, run) where
import Network
import System.Environment
import Control.Concurrent
import Control.Concurrent.MVar
import Control.Concurrent.Thread.Delay
import Control.Monad
import System.IO
import Data.Char

-- Attempts to connect to a host on a specified port
-- If successful, a thread is started that prints out
-- incoming data, a thread starts that takes input from
-- standard in, and a thread starts that pings the server
-- every 30 seconds. Once the message "disconnect" is received
-- from the server, each thread stops and the program exits.
run :: HostName -> Int -> IO ()
run host port = do
  handle <- connectTo host (PortNumber (fromIntegral port))
  stopVar <- newEmptyMVar
  ioo <- forkIO $ forever (doOutput handle)
  ioi <- forkIO $ forever (doInput handle stopVar)
  iom <- forkIO $ forever (maintainConnection handle)
  readMVar stopVar
  killThread ioo
  killThread ioi
  killThread iom
  return ()
  
-- Given a handle, takes a line from standard in and
-- sends it to the handle. If the message starts with a '/'
-- it is sent normally, if it does not start with '/' a
-- '/message ' is appended to the front of it before it is
-- sent.
doOutput :: Handle -> IO ()
doOutput handle = do
  msg <- getLine
  case msg of
    msg@('/':_) -> hPutStrLn handle msg
    msg -> hPutStrLn handle $ "/message " ++ msg

-- Given a handle for sending output, reads a line and
-- prints it to stdout. If the input is "disconnected"
-- the mvar is set which allows the main thread to die.
doInput :: Handle -> MVar Bool -> IO ()
doInput handle mvar = do
  string <- hGetLine handle
  putStrLn string
  if string == "disconnected" then putMVar mvar True else return ()

-- Sends the '/ping silent' command to the server
-- every 30 seconds
maintainConnection :: Handle -> IO ()
maintainConnection handle = do
  hPutStrLn handle "/ping silent"
  delay (1000 * 1000 * 30) -- 30 seconds

main = do
  args <- getArgs
  case args of
    [] -> printUsage
    (x:[]) -> printUsage
    (host:port:_) -> if isInt port then run host (read port) else printUsage
                                                   
printUsage :: IO ()                                                   
printUsage = putStrLn "./ChatClient hostname port"

isInt :: [Char] -> Bool
isInt = (== []) . (filter (not . isDigit))