cHelp :: [CmdlCmdDescription] -> CmdlState -> IO CmdlState
cHelp allcmds state = do
  putStrLn $ formatLine ("Command", "Parameter", "Description")
  putStrLn $ replicate maxLineWidth '-'
  mapM_ (\ cm -> do
                   let cmd = cmdDescription cm
                       name = cmdNameStr cmd
                       req = formatRequirement $ cmdReq cm
                       descL = formatDesc $ describeCmd cmd
                       desc = head descL ++
                              concatMap (('\n' : replicate descStart ' ') ++)
                              (tail descL)
                   putStrLn $ formatLine (name, req, desc)) allcmds
  return state
  where
    maxLineWidth = 80
    maxNameLen = maximum $ map (length . cmdNameStr . cmdDescription) allcmds
    maxParamLen = maximum $ map (length . formatRequirement . cmdReq ) allcmds
    descStart = maxNameLen + 1 + maxParamLen + 1
    descWidth = maxLineWidth - descStart
    formatDesc :: String -> [String]
    formatDesc = reverse . filter (not . null) . map trim .
                 foldl (\ l w -> if length (head l) + length w > descWidth
                                   then (w ++ " ") : l
                                   else (head l ++ w ++ " ") : tail l)
                       [""] . words
    formatLine :: (String, String, String) -> String
    formatLine (c1, c2, c3) =
      c1 ++ replicate (maxNameLen - length c1 + 1) ' ' ++
      c2 ++ replicate (maxParamLen - length c2 + 1) ' ' ++ c3
cHelp :: [CmdlCmdDescription] -> CmdlState -> IO CmdlState
cHelp allcmds state = do
  putStrLn $ formatLine ("Command", "Parameter", "Description")
  putStrLn $ replicate maxLineWidth '-'
  mapM_ (\ cm -> do
                   let cmd = cmdDescription cm
                       name = cmdNameStr cmd
                       req = formatRequirement $ cmdReq cm
                       descL = formatDesc $ describeCmd cmd
                       desc = head descL ++
                              concatMap (('\n' : replicate descStart ' ') ++)
                              (tail descL)
                   putStrLn $ formatLine (name, req, desc)) allcmds
  return state
  where
    maxLineWidth = 80
    maxNameLen = maximum $ map (length . cmdNameStr . cmdDescription) allcmds
    maxParamLen = maximum $ map (length . formatRequirement . cmdReq ) allcmds
    descStart = maxNameLen + 1 + maxParamLen + 1
    descWidth = maxLineWidth - descStart
    formatDesc :: String -> [String]
    formatDesc = reverse . filter (not . null) . map trim .
                 foldl (\ l w -> if length (head l) + length w > descWidth
                                   then (w ++ " ") : l
                                   else (head l ++ w ++ " ") : tail l)
                       [""] . words
    formatLine :: (String, String, String) -> String
    formatLine (c1, c2, c3) =
      c1 ++ replicate (maxNameLen - length c1 + 1) ' ' ++
      c2 ++ replicate (maxParamLen - length c2 + 1) ' ' ++ c3
cHelp allcmds state = do
  putStrLn $ formatLine ("Command", "Parameter", "Description")
  putStrLn $ replicate maxLineWidth '-'
  mapM_ (\ cm -> do
                   let cmd = cmdDescription cm
                       name = cmdNameStr cmd
                       req = formatRequirement $ cmdReq cm
                       descL = formatDesc $ describeCmd cmd
                       desc = head descL ++
                              concatMap (('\n' : replicate descStart ' ') ++)
                              (tail descL)
                   putStrLn $ formatLine (name, req, desc)) allcmds
  return state
  where
    maxLineWidth = 80
    maxNameLen = maximum $ map (length . cmdNameStr . cmdDescription) allcmds
    maxParamLen = maximum $ map (length . formatRequirement . cmdReq ) allcmds
    descStart = maxNameLen + 1 + maxParamLen + 1
    descWidth = maxLineWidth - descStart
    formatDesc :: String -> [String]
    formatDesc = reverse . filter (not . null) . map trim .
                 foldl (\ l w -> if length (head l) + length w > descWidth
                                   then (w ++ " ") : l
                                   else (head l ++ w ++ " ") : tail l)
                       [""] . words
    formatLine :: (String, String, String) -> String
    formatLine (c1, c2, c3) =
      c1 ++ replicate (maxNameLen - length c1 + 1) ' ' ++
      c2 ++ replicate (maxParamLen - length c2 + 1) ' ' ++ c3