import Data.Char (toUpper)
import Data.Function (on)
import Data.List (groupBy, sortBy)
import Data.Maybe (fromMaybe, isJust, isNothing)

toKey :: Char -> Maybe Char
toKey ch
  | ch < 'A' = Nothing
  | ch < 'D' = Just '2'
  | ch < 'G' = Just '3'
  | ch < 'J' = Just '4'
  | ch < 'M' = Just '5'
  | ch < 'P' = Just '6'
  | ch < 'T' = Just '7'
  | ch < 'W' = Just '8'
  | ch <= 'Z' = Just '9'
  | otherwise = Nothing

toKeyString :: String -> Maybe String
toKeyString st
  | any isNothing mch = Nothing
  | otherwise = Just $ map (fromMaybe '!') mch
  where
    mch = map (toKey . toUpper) st

showTextonym :: [(String, String)] -> String
showTextonym ts =
  fst (head ts)
    ++ " => "
    ++ concat
      [ w ++ " "
        | (_, w) <- ts
      ]

main :: IO ()
main = do
  let src = "unixdict.txt"
  contents <- readFile src
  let wordList = lines contents
      keyedList =
        [ (key, word)
          | (Just key, word) <-
              filter (isJust . fst) $
                zip (map toKeyString wordList) wordList
        ]
      groupedList =
        groupBy ((==) `on` fst) $
          sortBy (compare `on` fst) keyedList
      textonymList = filter ((> 1) . length) groupedList
  mapM_ putStrLn $
    [ "There are "
        ++ show (length keyedList)
        ++ " words in "
        ++ src
        ++ " which can be represented by the digit key mapping.",
      "They require "
        ++ show (length groupedList)
        ++ " digit combinations to represent them.",
      show (length textonymList) ++ " digit combinations represent Textonyms.",
      "",
      "Top 5 in ambiguity:"
    ]
      ++ fmap
        showTextonym
        ( take 5 $
            sortBy (flip compare `on` length) textonymList
        )
      ++ ["", "Top 5 in length:"]
      ++ fmap
        showTextonym
        (take 5 $ sortBy (flip compare `on` (length . fst . head)) textonymList)