import System.Environment (getArgs)
import Network
import Control.Concurrent
import System.IO
import Data.Char
import qualified HSH
import qualified Data.DList as D
import Control.Concurrent.MVar

defaultPort = 8080

main = do
  args <- getArgs
  msgsVar <- newMVar D.empty
  let port = fromInteger $ case args of
        portStr:_ -> read portStr
        _ -> defaultPort
      mode = if "--eliza" `elem` args
             then eliza
             else if "--messages" `elem` args
                  then messages msgsVar
                  else echo
  putStrLn $ "Listening on port " ++ show port
  sock <- listenOn $ PortNumber port
  acceptConnections sock mode

acceptConnections :: Socket -> (Handle -> IO ()) -> IO b
acceptConnections sock func = do
  (h, hostName, port) <- accept sock
  forkIO $ func h
  acceptConnections sock func

hInteractIO :: (String -> IO String) -> Handle -> IO ()
hInteractIO f h = do
  c <- hGetContents h
  hSetBuffering h LineBuffering
  mapM_ handleLine (lines c)
  hClose h
  where handleLine line = do putStrLn line
                             hPutStrLn h =<< f line
  
echo :: Handle -> IO ()
echo = hInteractIO return

trim :: String -> String
trim = reverse . dropWhile isSpace . reverse . dropWhile isSpace

eliza :: Handle -> IO ()
eliza = hInteractIO callEliza
  where callEliza str = if trim str /= ""
                        then fmap trim $ HSH.run $ HSH.echo str HSH.-|- "python2 ./eliza.py"
                        else return "TRY A NON-EMPTY STRING!"
        
messages :: MVar (D.DList String) -> Handle -> IO ()
messages var = hInteractIO messageBack
  where messageBack str = do msgs <- takeMVar var
                             let (m, msgs') = if null $ D.toList msgs
                                              then ("Hello World!", D.singleton str)
                                              else (D.head msgs, D.tail msgs `D.snoc` str)
                             putMVar var msgs'
                             return m