-- | Summaries are kept in a text file, @summaries.txt@ in plush's data dir.
-- Each command summary is just three parts: The "name" line, the "synopsis"
-- line(s), and then lines for the options. See the file for more details on
-- the formatting.
parseSummaries :: T.Text -> [(String, CommandSummary)]
parseSummaries = catMaybes . map build . splitOn divider . map T.strip . T.lines
  where
    build t = case coallesce $ splitOn T.null t of
        []       -> Nothing
        [n]      -> build' n T.empty []
        [n,s]    -> build' n s []
        (n:s:os) -> build' n s os
    build' n s os = Just (name n, CommandSummary n s (opts os))

    name = T.unpack . T.concat . take 1 . T.words

    opts os = catMaybes $ map opt $ concatMap T.lines os
    opt o | T.null o = Nothing
          | otherwise = let (flags, desc) = T.breakOn "  " o
                        in Just (T.splitOn ", " flags, T.stripStart desc)

    splitOn p = splitOn' []
      where
        splitOn' [] [] = []
        splitOn' r [] = reverse r : []
        splitOn' r (a:as) | p a = reverse r : splitOn' [] as
                          | otherwise = splitOn' (a:r) as

    coallesce ([]:as) = coallesce as
    coallesce (a:as) = T.strip (T.unlines a) : coallesce as
    coallesce [] = []

    divider t = not (T.null t) && T.all (== '-') t

-- | Prepare the options summary as a series of lines, nicely formatted for
-- textual output.
parseSummaries :: T.Text -> [(String, CommandSummary)]
parseSummaries = catMaybes . map build . splitOn divider . map T.strip . T.lines
  where
    build t = case coallesce $ splitOn T.null t of
        []       -> Nothing
        [n]      -> build' n T.empty []
        [n,s]    -> build' n s []
        (n:s:os) -> build' n s os
    build' n s os = Just (name n, CommandSummary n s (opts os))

    name = T.unpack . T.concat . take 1 . T.words

    opts os = catMaybes $ map opt $ concatMap T.lines os
    opt o | T.null o = Nothing
          | otherwise = let (flags, desc) = T.breakOn "  " o
                        in Just (T.splitOn ", " flags, T.stripStart desc)

    splitOn p = splitOn' []
      where
        splitOn' [] [] = []
        splitOn' r [] = reverse r : []
        splitOn' r (a:as) | p a = reverse r : splitOn' [] as
                          | otherwise = splitOn' (a:r) as

    coallesce ([]:as) = coallesce as
    coallesce (a:as) = T.strip (T.unlines a) : coallesce as
    coallesce [] = []

    divider t = not (T.null t) && T.all (== '-') t

-- | Prepare the options summary as a series of lines, nicely formatted for
-- textual output.
parseSummaries = catMaybes . map build . splitOn divider . map T.strip . T.lines
  where
    build t = case coallesce $ splitOn T.null t of
        []       -> Nothing
        [n]      -> build' n T.empty []
        [n,s]    -> build' n s []
        (n:s:os) -> build' n s os
    build' n s os = Just (name n, CommandSummary n s (opts os))

    name = T.unpack . T.concat . take 1 . T.words

    opts os = catMaybes $ map opt $ concatMap T.lines os
    opt o | T.null o = Nothing
          | otherwise = let (flags, desc) = T.breakOn "  " o
                        in Just (T.splitOn ", " flags, T.stripStart desc)

    splitOn p = splitOn' []
      where
        splitOn' [] [] = []
        splitOn' r [] = reverse r : []
        splitOn' r (a:as) | p a = reverse r : splitOn' [] as
                          | otherwise = splitOn' (a:r) as

    coallesce ([]:as) = coallesce as
    coallesce (a:as) = T.strip (T.unlines a) : coallesce as
    coallesce [] = []

    divider t = not (T.null t) && T.all (== '-') t

-- | Prepare the options summary as a series of lines, nicely formatted for
-- textual output.