module GF.Compile.Tags
         ( writeTags
         , gf2gftags
         ) where

import GF.Infra.Option
import GF.Infra.UseIO
import GF.Data.Operations
import GF.Grammar

--import Data.List
import qualified Data.Map as Map
import qualified Data.Set as Set
--import Control.Monad
import GF.Text.Pretty
import System.FilePath

writeTags opts gr file mo = do
  let imports = getImports opts gr mo
      locals  = getLocalTags [] mo
      txt     = unlines ((Set.toList . Set.fromList) (imports++locals))
  putPointE Normal opts ("  write file" +++ file) $ liftIO $ writeFile file txt

getLocalTags x (m,mi) = 
  [showIdent i ++ "\t" ++ k ++ "\t" ++ l ++ "\t" ++ t 
       | (i,jment) <- Map.toList (jments mi),
         (k,l,t)   <- getLocations jment] ++ x
  where
    getLocations :: Info -> [(String,String,String)]
    getLocations (AbsCat mb_ctxt)               = maybe (loc "cat")          mb_ctxt
    getLocations (AbsFun mb_type mb_eqs)        = maybe (ltype "fun")        mb_type ++
                                                  maybe (list (loc "def") . snd) mb_eqs  
    getLocations (ResParam mb_params _)         = maybe (loc "param")        mb_params
    getLocations (ResValue mb_type _)           = ltype "param-value"          mb_type
    getLocations (ResOper  mb_type mb_def)      = maybe (ltype "oper-type")  mb_type ++
                                                  maybe (loc "oper-def")     mb_def
    getLocations (ResOverload _ defs)           = list (\(x,y) -> ltype "overload-type" x ++ 
                                                                  loc   "overload-def"  y) defs
    getLocations (CncCat mty md mr mprn _)      = maybe (loc "lincat")       mty ++ 
                                                  maybe (loc "lindef")       md  ++
                                                  maybe (loc "linref")       mr  ++
                                                  maybe (loc "printname")    mprn
    getLocations (CncFun _ mlin mprn _)         = maybe (loc "lin")          mlin ++
                                                  maybe (loc "printname")    mprn
    getLocations _                              = []

    loc kind (L loc _) = [(kind,render (ppLocation (msrc mi) loc),"")]

    ltype kind (L loc ty) = [(kind,render (ppLocation (msrc mi) loc),render (ppTerm Unqualified 0 ty))]

    maybe f (Just x) = f x
    maybe f Nothing  = []

    list f xs = concatMap f xs
    
    render = renderStyle style{mode=OneLineMode}


getImports opts gr mo@(m,mi) = concatMap toDep allOpens
  where
    allOpens = [(OSimple m,incl) | (m,incl) <- mextend mi] ++ 
               [(o,MIAll) | o <- mopens mi]

    toDep (OSimple m,incl)     =
      let Ok mi = lookupModule gr m
      in [showIdent id ++ "\t" ++ "indir" ++ "\t" ++ render m ++ "\t\t" ++ gf2gftags opts (orig mi info)
            | (id,info) <- Map.toList (jments mi), filter incl id]
    toDep (OQualif m1 m2,incl) =
      let Ok mi = lookupModule gr m2
      in [showIdent id ++ "\t" ++ "indir" ++ "\t" ++ render m2 ++ "\t" ++ render m1 ++ "\t" ++ gf2gftags opts (orig mi info)
            | (id,info) <- Map.toList (jments mi), filter incl id]

    filter MIAll          id = True
    filter (MIOnly   ids) id = elem id ids
    filter (MIExcept ids) id = not (elem id ids)

    orig mi info =
      case info of
        AnyInd _ m0 -> let Ok mi0 = lookupModule gr m0
                       in msrc mi0
        _           ->    msrc mi 

gftagsFile :: FilePath -> FilePath
gftagsFile f = addExtension f "gf-tags"

gf2gftags :: Options -> FilePath -> FilePath
gf2gftags opts file = maybe (gftagsFile (dropExtension file))
                            (\dir -> dir </> gftagsFile (dropExtension (takeFileName file)))
                            (flag optOutputDir opts)
