import Ircmsg
import Mdl

import M_jankequote
import M_lokstatus
import M_bintopic
import M_src
import M_tschunk
import M_moin
import M_ceds
import M_opssau
import M_advent

import System.IO
import System.Time
import System.Locale (defaultTimeLocale)
import Control.Exception
import Control.Monad
import Network
import qualified Data.Map as Map

import Prelude hiding (log)


-- bot config:
botnick  = "karlhans"
botchans = "#karlhans"
modules  = [ tschunk botchans
           , autojankequote
           , lok
           , bintopic
           , src
           , opssau "now!" botchans
           ]


log n x = hPutStrLn stderr (show n ++ " " ++ x)

main
  = do log 0 "karlhans rising."
       inp <- atomically newTChan
       out <- atomically newTChan
       -- start up all modules
       let start m = do inp' <- atomically (dupTChan inp)
                        forkIO (m log inp' out)
       mapM_ start (stdmdls botnick botchans ++ modules)
       -- run
       forkIO (sender out)
       receiver inp
       log 0 "terminating botbrain."

receiver inp = do r_mp <- newIORef Map.empty
                  handleJust ioErrors hdlio (loop r_mp)
  where
  loop r_mp
    = do l <- getLine
         unless (null l) $
           handle hdl $
           do m <- case parse p_message (filter (/='\r') l) l of
                        Left  e -> log 0 (show e) >> return (Raw l)
                        Right x -> return x
              t <- getClockTime
              mp <- readIORef r_mp
              let (mp', accept) = limiter mp t m
              writeIORef r_mp mp'
              if accept then (do log 2 ("  " ++ show m)
                                 atomically (writeTChan inp m))
                        else (do let Msg _ u h _ _ = m
                                 log 2 (u++"@"++h++" coming too fast, ignored"))
         loop r_mp
  
  limiter mp t (Msg _ usr hst cmd _)
    | cmd `elem` ["PRIVMSG","NOTICE"]
      = case mt0 of Nothing -> (mp', True)
                    Just t0 -> (mp', diffsec t t0 >= 1)
    | otherwise = (mp, True)
    where
    mt0 = Map.lookup (usr,hst) mp
    mp' = Map.insert (usr,hst) t mp
  limiter mp t _ = (mp, True)
  
  hdl e = log 2 ("receiver: " ++ show e)
  hdlio e = log 2 ("receiver: " ++ show e ++ ", shutting down.")

diffsec :: ClockTime -> ClockTime -> Double
diffsec (TOD s1 p1) (TOD s2 p2)
  = fromIntegral (s1-s2) + fromIntegral (p1-p2) * 1e-12

sender out
  = handleJust ioErrors hdlio $
    do l <- atomically (readTChan out)
       handle hdl $
         do putStrLn (take 512 (l ++ "\r\n"))
            hFlush stdout
            -- make very sure we don't send more than 5 lines/s :)
            threadDelay 200000
       sender out
  where
  hdl e = log 2 ("sender: " ++ show e)
  hdlio e = log 2 ("sender: " ++ show e ++ ", shutting down.")
