{-# LANGUAGE LambdaCase #-}
module Main (main) where

import Control.Applicative    (liftA2, many, (<|>))
import Control.Monad          (void)
import Data.Foldable          (for_)
import Data.List              (sortBy)
import Data.Maybe             (fromMaybe)
import Data.Ord               (comparing)
import Language.Haskell.Lexer (PosToken, Token (..), lexerPass0)
import System.Environment     (getArgs)
import Text.Regex.Applicative (RE)

import qualified Data.Map.Strict        as Map
import qualified Text.Regex.Applicative as RE

main :: IO ()
main = do
    args <- getArgs

    data_ <- traverse processFile args

    putStrLn "Modules"
    let modules = sortBy (flip $ comparing snd) $ Map.toList $ Map.fromListWith (+)
          [ (mn, 1 :: Int)
          | xs <- data_
          , (mn, _) <- xs
          ]

    for_ (take 30 modules) $ \(mn, n) ->
        putStrLn $ mn ++ "  " ++ show n

    putStrLn ""

    putStrLn "Symbols"
    let symbols = sortBy (flip $ comparing snd) $ Map.toList $ Map.fromListWith (+)
          [ ((mn,sym), 1 :: Int)
          | xs <- data_
          , (mn, syms) <- xs
          , sym <- syms
          ]

    for_ (take 50 symbols) $ \((mn,sym), n) ->
        putStrLn $ mn ++ "." ++ sym ++ "  " ++ show n

processFile :: FilePath -> IO [(String, [String])]
processFile fp = do
    contents <- readFile fp
    let tokens = filter (\(t, _) -> t `notElem` [Whitespace, Comment, Commentstart, NestedComment])
               $ lexerPass0 contents

    return $ fromMaybe [] $ RE.match (somewhere imports) tokens

imports :: RE PosToken (String, [String])
imports = (,)
    <$ reservedid "import" <*> (conid <|> qconid) <*> msymbols
  where
    msymbols :: RE PosToken [String]
    msymbols =special "(" *> symbols <* special ")" <|> pure []

    symbols :: RE PosToken [String]
    symbols = liftA2 (:) symbol $ many (special "," *> symbol)

    symbol :: RE PosToken String
    symbol = varid <|> special "(" *> varsym <* special ")"


-------------------------------------------------------------------------------
-- regex-applicative + haskell-lexer
-------------------------------------------------------------------------------

anything :: RE s ()
anything = void $ RE.few RE.anySym

somewhere :: RE s a -> RE s [a]
somewhere re = anything *> RE.few (re <* anything)

reservedid :: String -> RE PosToken ()
reservedid k = RE.msym $ \case
    (Reservedid, (_, k')) | k == k' -> Just ()
    _                               -> Nothing

special :: String -> RE PosToken ()
special k = RE.msym $ \case
    (Special, (_, k')) | k == k' -> Just ()
    _                            -> Nothing

conid :: RE PosToken String
conid = RE.msym $ \case
    (Conid, (_, k)) -> Just k
    _               -> Nothing

qconid :: RE PosToken String
qconid = RE.msym $ \case
    (Qconid, (_, k)) -> Just k
    _                -> Nothing

varid :: RE PosToken String
varid = RE.msym $ \case
    (Varid, (_, k)) -> Just k
    _               -> Nothing

varsym :: RE PosToken String
varsym = RE.msym $ \case
    (Varsym, (_, k)) -> Just k
    _                -> Nothing
