content: {-# LANGUAGE PatternGuards #-}
  -- | A todo list
  --
  -- (c) 2005 Samuel Bronson
  module Lambdabot.Plugin.Misc.Todo (todoPlugin) where
  
  import Lambdabot.Compat.PackedNick
  import Lambdabot.Plugin
  import Control.Monad
  import qualified Data.ByteString.Char8 as P
  
  -- A list of key/elem pairs with an ordering determined by its position in the list
  type TodoState = [(P.ByteString, P.ByteString)]
  type Todo = ModuleT TodoState LB
  
  todoPlugin :: Module TodoState
  todoPlugin = newModule
      { moduleDefState  = return ([] :: TodoState)
      , moduleSerialize = Just assocListPackedSerial
  
      , moduleCmds = return
          [ (command "todo")
              { help = say "todo. List todo entries"
              , process = getTodo
              }
          , (command "todo-add")
              { help = say "todo-add <idea>. Add a todo entry"
              , process = addTodo
              }
          , (command "todo-delete")
              { privileged = True
              , help = say "todo-delete <n>. Delete a todo entry (for admins)"
              , process = delTodo
              }
          ]
      }
  
  -- | Print todo list
  getTodo :: String -> Cmd Todo ()
  getTodo [] = readMS >>= sayTodo
  getTodo _  = say "@todo has no args, try @todo-add or @list todo"
  
  -- | Pretty print todo list
  sayTodo :: [(P.ByteString, P.ByteString)] -> Cmd Todo ()
  sayTodo [] = say "Nothing to do!"
  sayTodo todoList = say . unlines =<< zipWithM fmtTodoItem ([0..] :: [Int]) todoList
      where
          fmtTodoItem n (idea, nick_) = do
              nick <- showNick (unpackNick nick_)
              return $ concat $
                  [ show n,". ", nick ,": ",P.unpack idea ]
  
  -- | Add new entry to list
  addTodo :: String -> Cmd Todo ()
  addTodo rest = do
      sender <- fmap packNick getSender
      modifyMS (++[(P.pack rest, sender)])
      say "Entry added to the todo list"
  
  -- | Delete an entry from the list
  delTodo :: String -> Cmd Todo ()
  delTodo rest
      | Just n <- readM rest = say =<< withMS (\ls write -> case () of
            _ | null ls -> return "Todo list is empty"
              | n > length ls - 1 || n < 0
              -> return (show n ++ " is out of range")
  
              | otherwise -> do
                  write (map snd . filter ((/= n) . fst) . zip [0..] $ ls)
                  let (a,_) = ls !! n
                  return ("Removed: " ++ P.unpack a))
  
      | otherwise = say "Syntax error. @todo <n>, where n :: Int"
  