doPRIVMSG' :: Nick -> IrcMessage -> Nick -> Base ()
doPRIVMSG' myname msg target
  | myname == target
    = let (cmd, params) = breakOnGlue " " text
      in doPersonalMsg cmd (dropWhile (== ' ') params)

  | flip any ":," $ \c -> (showNick msg myname ++ [c]) `isPrefixOf` text
    = let Just wholeCmd = maybeCommand (showNick msg myname) text
          (cmd, params) = breakOnGlue " " wholeCmd
      in doPublicMsg cmd (dropWhile (==' ') params)

  | (commands `arePrefixesOf` text)
  && length text > 1
  && (text !! 1 /= ' ') -- elem of prefixes
  && (not (commands `arePrefixesOf` [text !! 1]) ||
      (length text > 2 && text !! 2 == ' ')) -- ignore @@ prefix, but not the @@ command itself
    = let (cmd, params) = breakOnGlue " " (dropWhile (==' ') text)
      in doPublicMsg cmd (dropWhile (==' ') params)

  | otherwise =  doContextualMsg text

  where
    text = tail (head (tail (body msg)))
    who = nick msg

    doPersonalMsg s r
        | commands `arePrefixesOf` s        = doMsg (tail s) r who
        | s `elem` (evalPrefixes config)    = doMsg "run"    r who
        | otherwise                         = (lift $ doIGNORE msg)

    doPublicMsg s r
        | commands `arePrefixesOf` s        = doMsg (tail s) r target
        | (evalPrefixes config) `arePrefixesWithSpaceOf` s = doMsg "run" r target -- TODO
        | otherwise                         = (lift $ doIGNORE msg)

    --
    -- normal commands.
    --
    -- check privledges, do any spell correction, dispatch, handling
    -- possible timeouts.
    --
    -- todo, refactor
    --
    doMsg cmd rest towhere = do
        let ircmsg = ircPrivmsg towhere
        allcmds <- getDictKeys ircCommands
        let ms      = filter (isPrefixOf cmd) allcmds
        case ms of
            [s] -> docmd s                  -- a unique prefix
            _ | cmd `elem` ms -> docmd cmd  -- correct command (usual case)
            _ | otherwise     -> case closests cmd allcmds of
                  (n,[s]) | n < e ,  ms == [] -> docmd s -- unique edit match
                  (n,ss)  | n < e || ms /= []            -- some possibilities
                          -> lift . ircmsg $ "Maybe you meant: "++showClean(nub(ms++ss))
                  _ -> docmd cmd         -- no prefix, edit distance too far
        where
            e = 3   -- edit distance cut off. Seems reasonable for small words

            fcmd  = P.pack cmd      -- TODO
            frest = P.pack rest

            docmd cmd' = do
              act <- bindModule0 . withPS towhere $ \_ _ -> do
                withModule ircCommands cmd'   -- Important.
                    (ircPrivmsg towhere "Unknown command, try @list")
                    (\m -> do
                        name'   <- getName
                        privs   <- gets ircPrivCommands
                        let illegal = disabledCommands config
                        ok      <- liftM2 (||) (return $ cmd' `notElem` (privs ++ illegal))
                                               (lift $ checkPrivs msg)
                        if not ok
                          then lift $ ircPrivmsg towhere "Not enough privileges"
                          else catchIrc

                            (do mstrs <- catchError
                                 (Right `fmap` fprocess_ m fcmd frest)
                                 (\ex -> case (ex :: IRCError) of -- dispatch
                                   (IRCRaised (NoMethodError _)) -> catchError
                                        (Left `fmap` process m msg towhere cmd' rest)
                                        (\ey -> case (ey :: IRCError) of -- dispatch
                                            (IRCRaised (NoMethodError _)) ->
                                                Left `fmap` process_ m cmd' rest
                                            _ -> throwError ey)
                                   _ -> throwError ex)

                                -- send off our strings/bytestrings
                                case mstrs of
                                    Right ps -> lift $ mapM_ (ircPrivmsgF towhere) ps

                                    Left  ms -> lift $ mapM_ (ircPrivmsg towhere) ms)

                            (lift . ircPrivmsg towhere .
                                (("Plugin `" ++ name' ++ "' failed with: ") ++) . show))
              lift $ act
    --
    -- contextual messages are all input that isn't an explicit command.
    -- they're passed to all modules (todo, sounds inefficient) for
    -- scanning, and any that implement 'contextual' will reply.
    --
    -- we try to run the contextual functions from all modules, on every
    -- non-command. better hope this is efficient.
    --
    -- Note how we catch any plugin errors here, rather than letting
    -- them bubble back up to the mainloop
    --
    doContextualMsg r = lift $ do
        withAllModules ( \m -> do
            act <- bindModule0 ( do
                            ms <- contextual m msg target r
                            lift $ mapM_ (ircPrivmsg target) ms
                   )
            name' <- getName
            lift $ catchIrc act (debugStrLn . (name' ++) .
                (" module failed in contextual handler: " ++) . show)
            )
        return ()

------------------------------------------------------------------------
doPRIVMSG' :: Nick -> IrcMessage -> Nick -> Base ()
doPRIVMSG' myname msg target
  | myname == target
    = let (cmd, params) = breakOnGlue " " text
      in doPersonalMsg cmd (dropWhile (== ' ') params)

  | flip any ":," $ \c -> (showNick msg myname ++ [c]) `isPrefixOf` text
    = let Just wholeCmd = maybeCommand (showNick msg myname) text
          (cmd, params) = breakOnGlue " " wholeCmd
      in doPublicMsg cmd (dropWhile (==' ') params)

  | (commands `arePrefixesOf` text)
  && length text > 1
  && (text !! 1 /= ' ') -- elem of prefixes
  && (not (commands `arePrefixesOf` [text !! 1]) ||
      (length text > 2 && text !! 2 == ' ')) -- ignore @@ prefix, but not the @@ command itself
    = let (cmd, params) = breakOnGlue " " (dropWhile (==' ') text)
      in doPublicMsg cmd (dropWhile (==' ') params)

  | otherwise =  doContextualMsg text

  where
    text = tail (head (tail (body msg)))
    who = nick msg

    doPersonalMsg s r
        | commands `arePrefixesOf` s        = doMsg (tail s) r who
        | s `elem` (evalPrefixes config)    = doMsg "run"    r who
        | otherwise                         = (lift $ doIGNORE msg)

    doPublicMsg s r
        | commands `arePrefixesOf` s        = doMsg (tail s) r target
        | (evalPrefixes config) `arePrefixesWithSpaceOf` s = doMsg "run" r target -- TODO
        | otherwise                         = (lift $ doIGNORE msg)

    --
    -- normal commands.
    --
    -- check privledges, do any spell correction, dispatch, handling
    -- possible timeouts.
    --
    -- todo, refactor
    --
    doMsg cmd rest towhere = do
        let ircmsg = ircPrivmsg towhere
        allcmds <- getDictKeys ircCommands
        let ms      = filter (isPrefixOf cmd) allcmds
        case ms of
            [s] -> docmd s                  -- a unique prefix
            _ | cmd `elem` ms -> docmd cmd  -- correct command (usual case)
            _ | otherwise     -> case closests cmd allcmds of
                  (n,[s]) | n < e ,  ms == [] -> docmd s -- unique edit match
                  (n,ss)  | n < e || ms /= []            -- some possibilities
                          -> lift . ircmsg $ "Maybe you meant: "++showClean(nub(ms++ss))
                  _ -> docmd cmd         -- no prefix, edit distance too far
        where
            e = 3   -- edit distance cut off. Seems reasonable for small words

            fcmd  = P.pack cmd      -- TODO
            frest = P.pack rest

            docmd cmd' = do
              act <- bindModule0 . withPS towhere $ \_ _ -> do
                withModule ircCommands cmd'   -- Important.
                    (ircPrivmsg towhere "Unknown command, try @list")
                    (\m -> do
                        name'   <- getName
                        privs   <- gets ircPrivCommands
                        let illegal = disabledCommands config
                        ok      <- liftM2 (||) (return $ cmd' `notElem` (privs ++ illegal))
                                               (lift $ checkPrivs msg)
                        if not ok
                          then lift $ ircPrivmsg towhere "Not enough privileges"
                          else catchIrc

                            (do mstrs <- catchError
                                 (Right `fmap` fprocess_ m fcmd frest)
                                 (\ex -> case (ex :: IRCError) of -- dispatch
                                   (IRCRaised (NoMethodError _)) -> catchError
                                        (Left `fmap` process m msg towhere cmd' rest)
                                        (\ey -> case (ey :: IRCError) of -- dispatch
                                            (IRCRaised (NoMethodError _)) ->
                                                Left `fmap` process_ m cmd' rest
                                            _ -> throwError ey)
                                   _ -> throwError ex)

                                -- send off our strings/bytestrings
                                case mstrs of
                                    Right ps -> lift $ mapM_ (ircPrivmsgF towhere) ps

                                    Left  ms -> lift $ mapM_ (ircPrivmsg towhere) ms)

                            (lift . ircPrivmsg towhere .
                                (("Plugin `" ++ name' ++ "' failed with: ") ++) . show))
              lift $ act
    --
    -- contextual messages are all input that isn't an explicit command.
    -- they're passed to all modules (todo, sounds inefficient) for
    -- scanning, and any that implement 'contextual' will reply.
    --
    -- we try to run the contextual functions from all modules, on every
    -- non-command. better hope this is efficient.
    --
    -- Note how we catch any plugin errors here, rather than letting
    -- them bubble back up to the mainloop
    --
    doContextualMsg r = lift $ do
        withAllModules ( \m -> do
            act <- bindModule0 ( do
                            ms <- contextual m msg target r
                            lift $ mapM_ (ircPrivmsg target) ms
                   )
            name' <- getName
            lift $ catchIrc act (debugStrLn . (name' ++) .
                (" module failed in contextual handler: " ++) . show)
            )
        return ()

------------------------------------------------------------------------
doPRIVMSG' myname msg target
  | myname == target
    = let (cmd, params) = breakOnGlue " " text
      in doPersonalMsg cmd (dropWhile (== ' ') params)

  | flip any ":," $ \c -> (showNick msg myname ++ [c]) `isPrefixOf` text
    = let Just wholeCmd = maybeCommand (showNick msg myname) text
          (cmd, params) = breakOnGlue " " wholeCmd
      in doPublicMsg cmd (dropWhile (==' ') params)

  | (commands `arePrefixesOf` text)
  && length text > 1
  && (text !! 1 /= ' ') -- elem of prefixes
  && (not (commands `arePrefixesOf` [text !! 1]) ||
      (length text > 2 && text !! 2 == ' ')) -- ignore @@ prefix, but not the @@ command itself
    = let (cmd, params) = breakOnGlue " " (dropWhile (==' ') text)
      in doPublicMsg cmd (dropWhile (==' ') params)

  | otherwise =  doContextualMsg text

  where
    text = tail (head (tail (body msg)))
    who = nick msg

    doPersonalMsg s r
        | commands `arePrefixesOf` s        = doMsg (tail s) r who
        | s `elem` (evalPrefixes config)    = doMsg "run"    r who
        | otherwise                         = (lift $ doIGNORE msg)

    doPublicMsg s r
        | commands `arePrefixesOf` s        = doMsg (tail s) r target
        | (evalPrefixes config) `arePrefixesWithSpaceOf` s = doMsg "run" r target -- TODO
        | otherwise                         = (lift $ doIGNORE msg)

    --
    -- normal commands.
    --
    -- check privledges, do any spell correction, dispatch, handling
    -- possible timeouts.
    --
    -- todo, refactor
    --
    doMsg cmd rest towhere = do
        let ircmsg = ircPrivmsg towhere
        allcmds <- getDictKeys ircCommands
        let ms      = filter (isPrefixOf cmd) allcmds
        case ms of
            [s] -> docmd s                  -- a unique prefix
            _ | cmd `elem` ms -> docmd cmd  -- correct command (usual case)
            _ | otherwise     -> case closests cmd allcmds of
                  (n,[s]) | n < e ,  ms == [] -> docmd s -- unique edit match
                  (n,ss)  | n < e || ms /= []            -- some possibilities
                          -> lift . ircmsg $ "Maybe you meant: "++showClean(nub(ms++ss))
                  _ -> docmd cmd         -- no prefix, edit distance too far
        where
            e = 3   -- edit distance cut off. Seems reasonable for small words

            fcmd  = P.pack cmd      -- TODO
            frest = P.pack rest

            docmd cmd' = do
              act <- bindModule0 . withPS towhere $ \_ _ -> do
                withModule ircCommands cmd'   -- Important.
                    (ircPrivmsg towhere "Unknown command, try @list")
                    (\m -> do
                        name'   <- getName
                        privs   <- gets ircPrivCommands
                        let illegal = disabledCommands config
                        ok      <- liftM2 (||) (return $ cmd' `notElem` (privs ++ illegal))
                                               (lift $ checkPrivs msg)
                        if not ok
                          then lift $ ircPrivmsg towhere "Not enough privileges"
                          else catchIrc

                            (do mstrs <- catchError
                                 (Right `fmap` fprocess_ m fcmd frest)
                                 (\ex -> case (ex :: IRCError) of -- dispatch
                                   (IRCRaised (NoMethodError _)) -> catchError
                                        (Left `fmap` process m msg towhere cmd' rest)
                                        (\ey -> case (ey :: IRCError) of -- dispatch
                                            (IRCRaised (NoMethodError _)) ->
                                                Left `fmap` process_ m cmd' rest
                                            _ -> throwError ey)
                                   _ -> throwError ex)

                                -- send off our strings/bytestrings
                                case mstrs of
                                    Right ps -> lift $ mapM_ (ircPrivmsgF towhere) ps

                                    Left  ms -> lift $ mapM_ (ircPrivmsg towhere) ms)

                            (lift . ircPrivmsg towhere .
                                (("Plugin `" ++ name' ++ "' failed with: ") ++) . show))
              lift $ act
    --
    -- contextual messages are all input that isn't an explicit command.
    -- they're passed to all modules (todo, sounds inefficient) for
    -- scanning, and any that implement 'contextual' will reply.
    --
    -- we try to run the contextual functions from all modules, on every
    -- non-command. better hope this is efficient.
    --
    -- Note how we catch any plugin errors here, rather than letting
    -- them bubble back up to the mainloop
    --
    doContextualMsg r = lift $ do
        withAllModules ( \m -> do
            act <- bindModule0 ( do
                            ms <- contextual m msg target r
                            lift $ mapM_ (ircPrivmsg target) ms
                   )
            name' <- getName
            lift $ catchIrc act (debugStrLn . (name' ++) .
                (" module failed in contextual handler: " ++) . show)
            )
        return ()

------------------------------------------------------------------------