--
-- Haddock - A Haskell Documentation Tool
--
-- (c) Simon Marlow 2003
--

module Haddock.Rename (
  runRnFM, -- the monad (instance of Monad)
  renameDoc, renameMaybeDoc, renameExportItems,
) where

import Haddock.Types

import GHC hiding ( NoLink )
import BasicTypes
import SrcLoc 
import Bag ( emptyBag )

import Data.Map ( Map )
import qualified Data.Map as Map hiding ( Map )
import Prelude hiding ( mapM )
import Data.Traversable ( mapM )

-- -----------------------------------------------------------------------------
-- Monad for renaming

-- The monad does two things for us: it passes around the environment for
-- renaming, and it returns a list of names which couldn't be found in 
-- the environment.

newtype GenRnM n a = 
  RnM { unRn :: (n -> (Bool, DocName))	-- name lookup function
             -> (a,[n])
      }

type RnM a = GenRnM Name a

instance Monad (GenRnM n) where
  (>>=) = thenRn
  return = returnRn   

returnRn :: a -> GenRnM n a
returnRn a   = RnM (\_ -> (a,[]))
thenRn :: GenRnM n a -> (a -> GenRnM n b) -> GenRnM n b
m `thenRn` k = RnM (\lkp -> case unRn m lkp of 
				(a,out1) -> case unRn (k a) lkp of
						(b,out2) -> (b,out1++out2))

getLookupRn :: RnM (Name -> (Bool, DocName))
getLookupRn = RnM (\lkp -> (lkp,[]))
outRn :: Name -> RnM ()
outRn name = RnM (\_ -> ((),[name]))

lookupRn :: (DocName -> a) -> Name -> RnM a
lookupRn and_then name = do
  lkp <- getLookupRn
  case lkp name of
	(False,maps_to) -> do outRn name; return (and_then maps_to)
	(True, maps_to) -> return (and_then maps_to)

runRnFM :: Map Name Name -> RnM a -> (a,[Name])
runRnFM env rn = unRn rn lkp 
  where lkp n = case Map.lookup n env of
		  Nothing -> (False, NoLink n) 
		  Just q  -> (True,  Link q)

runRn :: (n -> (Bool,DocName)) -> GenRnM n a -> (a,[n])
runRn lkp rn = unRn rn lkp

-- -----------------------------------------------------------------------------
-- Renaming 

keep n = NoLink n
keepL (L loc n) = L loc (NoLink n)

rename = lookupRn id 
renameL (L loc name) = return . L loc =<< rename name

renameExportItems :: [ExportItem Name] -> RnM [ExportItem DocName]
renameExportItems items = mapM renameExportItem items

renameMaybeDoc :: Maybe (HsDoc Name) -> RnM (Maybe (HsDoc DocName))
renameMaybeDoc mbDoc = mapM renameDoc mbDoc

renameLDoc (L loc doc) = return . L loc =<< renameDoc doc

renameDoc :: HsDoc Name -> RnM (HsDoc DocName)
renameDoc doc = case doc of
  DocEmpty -> return DocEmpty
  DocAppend a b -> do
    a' <- renameDoc a
    b' <- renameDoc b
    return (DocAppend a' b')
  DocString str -> return (DocString str)
  DocParagraph doc -> do
    doc' <- renameDoc doc
    return (DocParagraph doc')
  DocIdentifier ids -> do
    lkp <- getLookupRn
    case [ n | (True, n) <- map lkp ids ] of
      ids'@(_:_) -> return (DocIdentifier ids')
      [] -> return (DocIdentifier (map NoLink ids))
  DocModule str -> return (DocModule str)
  DocEmphasis doc -> do
    doc' <- renameDoc doc
    return (DocEmphasis doc')
  DocMonospaced doc -> do
    doc' <- renameDoc doc
    return (DocMonospaced doc')
  DocUnorderedList docs -> do
    docs' <- mapM renameDoc docs
    return (DocUnorderedList docs')
  DocOrderedList docs -> do
    docs' <- mapM renameDoc docs
    return (DocOrderedList docs')
  DocDefList docs -> do
    docs' <- mapM (\(a,b) -> do
      a' <- renameDoc a
      b' <- renameDoc b
      return (a',b')) docs
    return (DocDefList docs')  
  DocCodeBlock doc -> do
    doc' <- renameDoc doc
    return (DocCodeBlock doc')
  DocURL str -> return (DocURL str) 
  DocAName str -> return (DocAName str)

renameLPred (L loc p) = return . L loc =<< renamePred p

renamePred :: HsPred Name -> RnM (HsPred DocName)
renamePred (HsClassP name types) = do
  name' <- rename name 
  types' <- mapM renameLType types
  return (HsClassP name' types')
renamePred (HsIParam (IPName name) t) = do
  name' <- rename name
  t' <- renameLType t
  return (HsIParam (IPName name') t')

renameLType (L loc t) = return . L loc =<< renameType t

renameType t = case t of 
  HsForAllTy expl tyvars lcontext ltype -> do
    tyvars' <- mapM renameLTyVarBndr tyvars
    lcontext' <- renameLContext lcontext 
    ltype' <- renameLType ltype
    return (HsForAllTy expl tyvars' lcontext' ltype')

  HsTyVar n -> return . HsTyVar =<< rename n
  HsBangTy b ltype -> return . HsBangTy b =<< renameLType ltype
  
  HsAppTy a b -> do
    a' <- renameLType a
    b' <- renameLType b
    return (HsAppTy a' b')

  HsFunTy a b -> do     
    a' <- renameLType a
    b' <- renameLType b
    return (HsFunTy a' b')

  HsListTy t -> return . HsListTy =<< renameLType t
  HsPArrTy t -> return . HsPArrTy =<< renameLType t

  HsTupleTy b ts -> return . HsTupleTy b =<< mapM renameLType ts

  HsOpTy a (L loc op) b -> do
    op' <- rename op
    a' <- renameLType a
    b' <- renameLType b
    return (HsOpTy a' (L loc op') b')

  HsParTy t -> return . HsParTy =<< renameLType t

  HsNumTy n -> return (HsNumTy n)

  HsPredTy p -> return . HsPredTy =<< renamePred p

  HsKindSig t k -> do
    t' <- renameLType t
    return (HsKindSig t' k)

  HsDocTy t doc -> do
    t' <- renameLType t
    doc' <- renameLDoc doc
    return (HsDocTy t' doc')

  _ -> error "renameType"

renameLTyVarBndr (L loc tv) = do
  name' <- rename (hsTyVarName tv)
  return $ L loc (replaceTyVarName tv name')
    
renameLContext (L loc context) = do
  context' <- mapM renameLPred context
  return (L loc context')

renameInstHead :: InstHead Name -> RnM (InstHead DocName)
renameInstHead (preds, className, types) = do
  preds' <- mapM renamePred preds
  className' <- rename className
  types' <- mapM renameType types
  return (preds', className', types')

renameLDecl (L loc d) = return . L loc =<< renameDecl d

renameDecl d = case d of
  TyClD d -> do
    d' <- renameTyClD d
    return (TyClD d')
  SigD s -> do
    s' <- renameSig s
    return (SigD s')
  ForD d -> do
    d' <- renameForD d
    return (ForD d')
  _ -> error "renameDecl"

renameTyClD d = case d of
  ForeignType _ _ _ -> error "renameTyClD" -- I'm guessing these can't be exported
 -- ForeignType name a b -> do
 --   name' <- renameL name
 --   return (ForeignType name' a b)

  TyData x lcontext lname ltyvars _ k cons _ -> do
    lcontext' <- renameLContext lcontext
    ltyvars' <- mapM renameLTyVarBndr ltyvars
    cons' <- mapM renameLCon cons
    -- I don't think we need the derivings, so we return Nothing
    -- We skip the type patterns too. TODO: find out what they are :-)
    return (TyData x lcontext' (keepL lname) ltyvars' Nothing k cons' Nothing) 
 
  TySynonym lname ltyvars typat ltype -> do
    ltyvars' <- mapM renameLTyVarBndr ltyvars
    ltype' <- renameLType ltype
    -- We skip type patterns here as well.
    return (TySynonym (keepL lname) ltyvars' Nothing ltype')

  ClassDecl lcontext lname ltyvars lfundeps lsigs _ _ _ -> do
    lcontext' <- renameLContext lcontext
    ltyvars' <- mapM renameLTyVarBndr ltyvars
    lfundeps' <- mapM renameLFunDep lfundeps 
    lsigs' <- mapM renameLSig lsigs
    -- we don't need the default methods or the already collected doc entities
    -- we skip the ATs for now.
    return (ClassDecl lcontext' (keepL lname) ltyvars' lfundeps' lsigs' emptyBag [] [])
 
  where
    renameLCon (L loc con) = return . L loc =<< renameCon con
    renameCon (ConDecl lname expl ltyvars lcontext details restype mbldoc) = do
      ltyvars' <- mapM renameLTyVarBndr ltyvars
      lcontext' <- renameLContext lcontext
      details' <- renameDetails details
      restype' <- renameResType restype
      mbldoc' <- mapM renameLDoc mbldoc
      return (ConDecl (keepL lname) expl ltyvars' lcontext' details' restype' mbldoc') 

    renameDetails (RecCon fields) = return . RecCon =<< mapM renameField fields
    renameDetails (PrefixCon ps) = return . PrefixCon =<< mapM renameLType ps
    renameDetails (InfixCon a b) = do
      a' <- renameLType a
      b' <- renameLType b
      return (InfixCon a' b')

    renameField (ConDeclField name t doc) = do
      t'   <- renameLType t
      doc' <- mapM renameLDoc doc
      return (ConDeclField (keepL name) t' doc')

    renameResType (ResTyH98) = return ResTyH98
    renameResType (ResTyGADT t) = return . ResTyGADT =<< renameLType t

    renameLFunDep (L loc (xs, ys)) = return (L loc (map keep xs, map keep ys))
   
    renameLSig (L loc sig) = return . L loc =<< renameSig sig
      
renameSig sig = case sig of 
  TypeSig (L loc name) ltype -> do 
    ltype' <- renameLType ltype
    return (TypeSig (L loc (keep name)) ltype')
{-  SpecSig lname ltype x -> do
    lname' <- renameL lname
    ltype' <- renameLType ltype
    return (SpecSig lname' ltype' x)
  InlineSig lname x -> do
    lname' <- renameL lname
    return (InlineSig lname' x)   
  SpecInstSig t -> return . SpecInstSig =<< renameLType t
  FixSig fsig -> return . FixSig =<< renameFixitySig fsig
  where
    renameFixitySig (FixitySig lname x) = do
      lname' <- renameL lname
      return (FixitySig lname' x)
-}

renameForD (ForeignImport lname ltype x) = do
  ltype' <- renameLType ltype
  return (ForeignImport (keepL lname) ltype' x)
renameForD (ForeignExport lname ltype x) = do
  ltype' <- renameLType ltype
  return (ForeignExport (keepL lname) ltype' x)

renameExportItem :: ExportItem Name -> RnM (ExportItem DocName)
renameExportItem item = case item of 
  ExportModule mod -> return (ExportModule mod)
  ExportGroup lev id doc -> do
    doc' <- renameDoc doc
    return (ExportGroup lev id doc')
  ExportDecl x decl doc instances -> do
    decl' <- renameLDecl decl
    doc' <- mapM renameDoc doc
    instances' <- mapM renameInstHead instances
    return (ExportDecl x decl' doc' instances')
  ExportNoDecl x y subs -> do
    y' <- lookupRn id y
    subs' <- mapM (lookupRn id) subs
    return (ExportNoDecl x y' subs')
  ExportDoc doc -> do
    doc' <- renameDoc doc
    return (ExportDoc doc')