module Halberd.ChosenImports where

import Control.Monad
import Data.List
import Data.Map (Map, insertWith)
import Data.Monoid
import Language.Haskell.Exts.Annotated hiding (name)
import qualified Data.Map                as Map
import qualified Distribution.ModuleName as Cabal
import qualified Distribution.Text       as Cabal

data ChosenImports = ChosenImports
  { qualifieds   :: Map (ModuleName ()) Cabal.ModuleName
  , unqualifieds :: Map Cabal.ModuleName [Name ()]
  } deriving (Show, Eq)

instance Monoid ChosenImports where
  mempty = ChosenImports
    { qualifieds   = mempty
    , unqualifieds = mempty
    }
  i1 `mappend` i2 = ChosenImports
    { qualifieds   = qualifieds   i1 `mappend`  qualifieds i2
    , unqualifieds = unqualifieds i1 `mappend`  unqualifieds i2
    }

lookupQualified :: ModuleName () -> ChosenImports -> Maybe Cabal.ModuleName
lookupQualified qualification = Map.lookup qualification . qualifieds

insertQualified :: ModuleName () -> Cabal.ModuleName -> ChosenImports -> ChosenImports
insertQualified qualification module_ chosenImports = chosenImports
  { qualifieds = Map.insert qualification module_ (qualifieds chosenImports) }

insertUnqualified :: Cabal.ModuleName -> Name () -> ChosenImports -> ChosenImports
insertUnqualified module_ name chosenImports = chosenImports
  { unqualifieds = insertWith (++) module_ [name] (unqualifieds chosenImports) }

insertChoice :: QName a -> Cabal.ModuleName -> ChosenImports -> ChosenImports
insertChoice qname module_ =
  case qname of
    Qual _ qualification _ -> insertQualified (void qualification) module_
    UnQual _ name          -> insertUnqualified module_ (void name)
    Special _ _            -> error "impossible: insertChoice"

isEmpty :: ChosenImports -> Bool
isEmpty ci = Map.null (qualifieds ci) && Map.null (unqualifieds ci)

showChosenImports :: ChosenImports -> [String]
showChosenImports ci = showQualifieds (qualifieds ci) ++ showUnqualifieds (unqualifieds ci)

showQualifieds :: Map (ModuleName ()) Cabal.ModuleName -> [String]
showQualifieds = map (uncurry showQualified) . Map.toList

showUnqualifieds :: Map Cabal.ModuleName [Name ()] -> [String]
showUnqualifieds = map (uncurry showUnqualified) . Map.toList

showQualified :: ModuleName () -> Cabal.ModuleName -> String
showQualified qualification modName =
    intercalate " "
      [ "import"
      , "qualified"
      , Cabal.display modName
      , "as"
      , prettyPrint qualification
      ]
showUnqualified :: Cabal.ModuleName -> [Name ()] -> String
showUnqualified modName names =
    intercalate " "
      [ "import"
      , Cabal.display modName
      , "("
      ,  intercalate ", " $ map prettyPrint names
      , ")"
      ]