module PfeDepCmds where
import Prelude hiding (print)
import List(nub,intersect)
import Monad(unless)

import HsName(HsName(..))
import HsIdent(getHSName)
--import HsConstants(main_name)
import SourceNames(SN(..))
import SrcLoc1(loc0,srcLoc,srcLine,srcColumn)
import PFEdeps(runPFE5,depModules',tdepModules')
import Pfe0Cmds(mainModules)
import PfeParse
import PFE0(allModules,pput,epput,getCurrentModuleGraph,getSubGraph,preparseModule)
import PFE2(getModuleExports)
import AbstractIO
import MUtils
import QualNames(getQualified)
import HasBaseName(getBaseName)
import DefinedNames(definedNames)
import UniqueNames(HasOrig(..),PN(..),Orig(..),noSrcLoc,origModule)
import PNT(PNT(..))
import TiPNT() -- instances for PNT
--import TiNames(instName)
import TypedIds(IdTy(..),idTy,NameSpace(..),namespace)
import Ents(Ent(..))
import Relations(applyRel)
import SimpleGraphs(graph,nodes,reachable,isReachable,listReachable,reverseGraph)
--import OrdGraph --slower than Graph!
import PrettyPrint

import PFE3(refsModule)
--import RefsTypes(isDef,shPos)
--import ConvRefsTypes(simplifyRefsTypes')

runPFE5Cmds ext = runCmds (runPFE5 ext)

pfeDepCmds =
    [("deps" ,        (tModuleArgs deps,
	               "compute dependency graph for definitions")),
     ("needed",       (tQualIds needed',"needed definitions")),
     ("neededmodules",(tQualIds neededmodules',
		       "names of modules containing needed definitions")),
     ("dead",         (tQualIds dead',"dead code (default: Main.main)")),
--   ("refs",         (moduleArgs refs,"list what idenfifiers refer to")),
     ("uses",         (entityId uses,"find uses of an entity"))]

tModuleArgs = moduleArgs' opts . uncurry
  where opts = (,) #@ dot <@ untyped
        dot = kwOption "-dot"

tQualIds cmd = f #@ untyped <@ many (arg "<M.x>")
  where f depM = cmd depM @@ parseQualIds

untyped = depM #@ kwOption "-untyped"
  where
    depM untyped optms = pick # depM' untyped optms
      where pick = maybe id (\ms -> filter(\ d@(m,ds)->m `elem` ms)) optms
    depM' untyped = if untyped then depModules' else tdepModules'

--needed = needed' depModules'
--tneeded = needed' tdepModules'

needed' dm qids =
  do (_,_,used) <- snd # depgraph' dm qids
     pput $ ppOrigs (listReachable used)

--neededmodules = neededmodules' depModules'
--tneededmodules = neededmodules' tdepModules'

neededmodules' dm qids =
  do (_,_,used) <- snd # depgraph' dm qids
     pput $ fsep (nub $ map origModule (listReachable used))

--dead = dead' depModules'
--tdead = dead' tdepModules'

dead' dm [] = do mains <- mainModules
                 dead'' dm [(m,"main")|m<-mains]
dead' dm qids = dead'' dm qids

dead'' dm qids =
  do (g,ids,used) <- snd # depgraph' dm qids
     let unused = [n|n<-nodes g,not (isReachable used n)]
     pput $ fsep (map ppOrig unused)

uses (optns,q@(m,n)) =
  do pnts <- definedPNTs m
     let ppq =  m<>"."<>n
     case pnts of
       [] -> fail.pp $ "No such"<+>ppns optns<>":"<+>ppq
       _ -> do let n=length pnts
               unless (n<2) $
                 epput$ ppq<+>"matches"<+>n<+>"entities, showing uses of all"
	       findUses pnts
  where
    ppns = maybe (pp "entity") ppns'
    ppns' ValueNames = pp "value"
    ppns' ClassOrTypeNames = "class or type"

    findUses pnts =
      mapM_ (usesIn pnts) . flip reachable [m] . reverseGraph . map snd
        =<< getCurrentModuleGraph

    definedPNTs =  map pnt . filter same . definedNames #. preparseModule
    same (i,ty) = getQualified (getBaseName (getHSName i)) == n
	          && maybe True (namespace ty ==) optns
    pnt (i,ty) = pnt' (getQualified # ty) m n

refs ms = mapM_ ref1 ms
  where
    ref1 = pput.ppRefs @@ refsModule
    ppRefs = vcat . map ppRef
    ppRef (_,r,os) = r<+>"at"<+>srcLoc r<>":" $$
		     nest 4 (vcat [pp (srcLoc o)|(o,_)<-os])

usesIn pnts m =
  do refs <- refsModule m
     let qs = sp pnts
         uses = [ppLineCol (srcLoc r)|(_,r,origs)<-refs,
                               --not (isDef r),
			       let os = sp (origs2PNT origs),
		               os `intersects` qs]
     unless (null uses) $ pput (m<>":"<+>fsep uses)
  where
    sp xs = [(x,namespace (idTy x))|x<-xs]
    origs2PNT origs =
      [PNT (getHSName pn) ty noSrcLoc|(pn,ty)<-origs]

ppLineCol p = srcLine p<>","<>srcColumn p

intersects xs ys = not . null $ intersect xs ys

depgraph = depgraph' depModules'
tdepgraph = depgraph' tdepModules'

depgraph' depModules' qids =
  do let ms = nub (map fst qids)
     deps <- depModules' . Just . map (fst.snd) =<< getSubGraph (Just ms)
     ids <- concatMapM origpnt qids
     let g = depGraph [(n,ns)|(m,(t,ds))<-deps,(ns1,(ns,h))<-ds,n<-ns1]
         always = [n|(m,(t,ds))<-deps,([],(ns,h))<-ds,n<-ns]
	 used = reachable g (nub (ids++always))
     return (deps,(g,ids,used))

depGraph = graph . mapSnd concat . collectByFst
                           -- because of names from typesigs...

origpnt (m,n) =
  do (t,rel) <- getModuleExports m
     return $ case map ent2pnt (applyRel rel (SN n loc0)) of
                [] -> [pnt m n]
                ns -> ns
  where
    ent2pnt (Ent m i ty) = pnt' ty m n
      where SN n _ = getHSName i
    pnt = pnt' Value

pnt' ty m n = PNT (PN (Qual m n) (g m n)) (conv # ty) noSrcLoc
  where
    conv (SN n s) = PN n (S s)
    g m n = G m n noSrcLoc

--dotdeps depM = pput.pdotdeps @@ depM . just
--tdotdeps = pput.pdotdeps @@ tdepModules' . just

pdotdeps deps =
    "digraph DepGraph"$$
    braces ("size=\"7.5,10.5\";ratio=fill;"$$
            vcat [p d<>"->"<>braces (fsep [p f<>";"|f<-fs])
                  |(m,(t,mdeps))<-deps,(ds,(fs,h))<-mdeps,d<-ds])
  where
    p = doubleQuotes . ppOrig

--tdeps = pput.vcat.map pdeps @@ tdepModules' . just

deps dot depM = pput.fmt @@ depM . just
  where
    fmt = if dot then pdotdeps else vcat.map pdeps

    pdeps  (m,(t,deps)) =
      sep ["module"<+>m<>":",nest 2 (vcat $ map pdep deps)]
      where
	pdep (ds,(fs,h)) = sep [fsep ds <> ":",nest 2 (fsep (map ppOrig fs))]
	ppOrig = ppOrig' (Just m)

ppOrig = ppOrig' Nothing

ppOrig' optm n =
    if Just m'==optm then ppi x else m'<>"."<>x
  where
    (m',x) = origId n

origId n =
    case orig n of
      G m' n' _ -> (m', n')
--    I m' loc  -> (m',instName m' loc)
      _ -> error ("Bug: PfeDepCmds.origId "++show n)

ppOrigs qs = vcat [m<>":"<+>fsep xs|(m,xs)<-collectByFst (map origId qs)]
