-- Copyright (c) 2006 Don Stewart - http://www.cse.unsw.edu.au/~dons
-- GPL version 2 or later (see http://www.gnu.org/copyleft/gpl.html)

{-# LANGUAGE FlexibleContexts #-}

-- | String and other utilities
module Lambdabot.Util (
        strip,
        dropFromEnd,
        splitFirstWord,
        limitStr,
        listToStr,
        showClean,
        expandTab,
        arePrefixesWithSpaceOf,
        arePrefixesOf,

        io,
        forkUnmasked,

        random,
        randomFailureMsg,
        randomSuccessMsg
    ) where

import Control.Concurrent.Lifted
import Control.Monad.Trans
import Control.Monad.Trans.Control
import Data.Char
import Data.List
import Data.Random
import Lambdabot.Config
import Lambdabot.Config.Core
import System.Random.Stateful (newIOGenM, newStdGen)

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

-- | Break a String into it's first word, and the rest of the string. Example:
--
-- > split_first_word "A fine day" ===> ("A", "fine day)
splitFirstWord :: String -- ^ String to be broken
                 -> (String, String)
splitFirstWord :: String -> (String, String)
splitFirstWord String
xs = (String
w, forall a. (a -> Bool) -> [a] -> [a]
dropWhile Char -> Bool
isSpace String
xs')
  where (String
w, String
xs') = forall a. (a -> Bool) -> [a] -> ([a], [a])
break Char -> Bool
isSpace String
xs

-- | Truncate a string to the specified length, putting ellipses at the
-- end if necessary.
limitStr :: Int -> String -> String
limitStr :: Int -> String -> String
limitStr Int
n String
s = let (String
b, String
t) = forall a. Int -> [a] -> ([a], [a])
splitAt Int
n String
s in
           if forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
t then String
b else forall a. Int -> [a] -> [a]
take (Int
nforall a. Num a => a -> a -> a
-Int
3) String
b forall a. [a] -> [a] -> [a]
++ String
"..."

-- | Form a list of terms using a single conjunction. Example:
--
-- > listToStr "and" ["a", "b", "c"] ===> "a, b and c"
listToStr :: String -> [String] -> String
listToStr :: String -> [String] -> String
listToStr String
_    []           = []
listToStr String
conj (String
item:[String]
items) =
  let listToStr' :: [String] -> String
listToStr' [] = []
      listToStr' [String
y] = forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [String
" ", String
conj, String
" ", String
y]
      listToStr' (String
y:[String]
ys) = forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [String
", ", String
y, [String] -> String
listToStr' [String]
ys]
  in  String
item forall a. [a] -> [a] -> [a]
++ [String] -> String
listToStr' [String]
items

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

-- | Pick a random element of the list.
random :: MonadIO m => [a] -> m a
random :: forall (m :: * -> *) a. MonadIO m => [a] -> m a
random [a]
l = do
  IOGenM StdGen
g <- forall (m :: * -> *) g. MonadIO m => g -> m (IOGenM g)
newIOGenM forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< forall (m :: * -> *). MonadIO m => m StdGen
newStdGen
  forall (d :: * -> *) (m :: * -> *) t g.
(Sampleable d m t, StatefulGen g m) =>
g -> d t -> m t
sampleFrom IOGenM StdGen
g (forall a. [a] -> RVar a
randomElement [a]
l)

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

-- | 'strip' takes as input a predicate and a list and strips
--   elements matching the predicate from the prefix as well as
--   the suffix of the list. Example:
--
-- > strip isSpace "   abc  " ===> "abc"
strip :: (a -> Bool) -> [a] -> [a]
strip :: forall a. (a -> Bool) -> [a] -> [a]
strip a -> Bool
p = let f :: [a] -> [a]
f = forall a. [a] -> [a]
reverse forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. (a -> Bool) -> [a] -> [a]
dropWhile a -> Bool
p in [a] -> [a]
f forall b c a. (b -> c) -> (a -> b) -> a -> c
. [a] -> [a]
f

-- | Drop elements matching a predicate from the end of a list
dropFromEnd :: (a -> Bool) -> [a] -> [a]
dropFromEnd :: forall a. (a -> Bool) -> [a] -> [a]
dropFromEnd a -> Bool
p = forall a. [a] -> [a]
reverse forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. (a -> Bool) -> [a] -> [a]
dropWhile a -> Bool
p forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [a] -> [a]
reverse

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

-- | show a list without heavyweight formatting
-- NB: assumes show instance outputs a quoted 'String'.
-- under that assumption, strips the outer quotes.
showClean :: (Show a) => [a] -> String
showClean :: forall a. Show a => [a] -> String
showClean = forall a. [a] -> [[a]] -> [a]
intercalate String
" " forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map (forall a. [a] -> [a]
init forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [a] -> [a]
tail forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Show a => a -> String
show)

-- | untab an string
expandTab :: Int -> String -> String
expandTab :: Int -> String -> String
expandTab Int
w = Int -> String -> String
go Int
0
  where
    go :: Int -> String -> String
go Int
_ []         = []
    go Int
i (Char
'\t':String
xs)  = forall a. Int -> a -> [a]
replicate (Int
w forall a. Num a => a -> a -> a
- Int
i forall a. Integral a => a -> a -> a
`mod` Int
w) Char
' ' forall a. [a] -> [a] -> [a]
++ Int -> String -> String
go Int
0 String
xs
    go Int
i (Char
x:String
xs)     = Char
x forall a. a -> [a] -> [a]
: Int -> String -> String
go (Int
iforall a. Num a => a -> a -> a
+Int
1) String
xs

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

-- convenience:
io :: MonadIO m => IO a -> m a
io :: forall (m :: * -> *) a. MonadIO m => IO a -> m a
io = forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO
{-# INLINE io #-}

forkUnmasked :: MonadBaseControl IO m => m () -> m ThreadId
forkUnmasked :: forall (m :: * -> *). MonadBaseControl IO m => m () -> m ThreadId
forkUnmasked m ()
m = forall (m :: * -> *).
MonadBaseControl IO m =>
((forall a. m a -> m a) -> m ()) -> m ThreadId
forkWithUnmask forall a b. (a -> b) -> a -> b
$ \forall a. m a -> m a
unmask -> forall a. m a -> m a
unmask m ()
m

arePrefixesWithSpaceOf :: [String] -> String -> Bool
arePrefixesWithSpaceOf :: [String] -> String -> Bool
arePrefixesWithSpaceOf = [String] -> String -> Bool
arePrefixesOf forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map (forall a. [a] -> [a] -> [a]
++ String
" ")

arePrefixesOf :: [String] -> String -> Bool
arePrefixesOf :: [String] -> String -> Bool
arePrefixesOf = forall a b c. (a -> b -> c) -> b -> a -> c
flip (forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b c. (a -> b -> c) -> b -> a -> c
flip forall a. Eq a => [a] -> [a] -> Bool
isPrefixOf)

--
-- Amusing insults from OpenBSD sudo
--
insult :: [String]
insult :: [String]
insult =
   [String
"Just what do you think you're doing Dave?",
    String
"It can only be attributed to human error.",
    String
"That's something I cannot allow to happen.",
    String
"My mind is going. I can feel it.",
    String
"Sorry about this, I know it's a bit silly.",
    String
"Take a stress pill and think things over.",
    String
"This mission is too important for me to allow you to jeopardize it.",
    String
"I feel much better now.",

    String
"Wrong!  You cheating scum!",
    String
"And you call yourself a Rocket Scientist!",
    String
"And you call yourself a Rocket Surgeon!",
    String
"Where did you learn to type?",
    String
"Are you on drugs?",
    String
"My pet ferret can type better than you!",
    String
"You type like i drive.",
    String
"Do you think like you type?",
    String
"Your mind just hasn't been the same since the electro-shock, has it?",
    String
"I don't think I can be your friend on Facebook anymore.",

    String
"Maybe if you used more than just two fingers...",
    String
"BOB says:  You seem to have forgotten your passwd, enter another!",
    String
"stty: unknown mode: doofus",
    String
"I can't hear you -- I'm using the scrambler.",
    String
"The more you drive -- the dumber you get.",
    String
"Listen, broccoli brains, I don't have time to listen to this trash.",
    String
"I've seen penguins that can type better than that.",
    String
"Have you considered trying to match wits with a rutabaga?",
    String
"You speak an infinite deal of nothing.",

    -- other
    String
"Are you typing with your feet?",
    String
"Abort, Retry, Panic?",

    -- More haskellish insults
    String
"You untyped fool!",
    String
"My brain just exploded"
    ]

--
-- some more friendly replies
--
apology :: [String]
apology :: [String]
apology =
   [String
"I am sorry.",String
"Sorry.",
    String
"Maybe you made a typo?",
    String
"Just try something else.",
    String
"There are some things that I just don't know.",
    String
"Whoa.",
    String
":(",String
":(",
    String
"",String
"",String
""
    ]

randomFailureMsg :: (MonadIO m, MonadConfig m) => m String
randomFailureMsg :: forall (m :: * -> *). (MonadIO m, MonadConfig m) => m String
randomFailureMsg = do
    Bool
useInsults <- forall (m :: * -> *) a. MonadConfig m => Config a -> m a
getConfig Config Bool
enableInsults
    forall (m :: * -> *) a. MonadIO m => [a] -> m a
random (if Bool
useInsults then [String]
insult forall a. [a] -> [a] -> [a]
++ [String]
apology else [String]
apology)

--
-- Some more interesting confirmations for @remember and @where
--
confirmation :: [String]
confirmation :: [String]
confirmation =
   [String
"Done.",String
"Done.",
    String
"Okay.",
    String
"I will remember.",
    String
"Good to know.",
    String
"It is stored.",
    String
"I will never forget.",
    String
"It is forever etched in my memory.",
    String
"Nice!"
   ]

randomSuccessMsg :: MonadIO m => m String
randomSuccessMsg :: forall (m :: * -> *). MonadIO m => m String
randomSuccessMsg = forall (m :: * -> *) a. MonadIO m => [a] -> m a
random [String]
confirmation