module RefacGenFold where

import PFE0
import HsTokens
import TypeCheck
import PrettyPrint
import PosSyntax
import HsName
import HsLexerPass1
import AbstractIO
import Maybe
import TypedIds
import UniqueNames hiding (srcLoc)
import PNT
import TiPNT
import List
import RefacUtils hiding (getParams)
import PFE0 (findFile, allFiles, allModules)
import MUtils (( # ))
import RefacLocUtils
import System
import IO
import Relations
import Ents
import Data.Set (toList)
import Data.List
import System.IO.Unsafe
import System.Cmd
import LocalSettings
import RefacSimplify
import StateMT(WithState,withSt,withStS,getSt,setSt,updSt,updSt_)
import qualified Control.Exception as CE (catch)
import EditorCommands
-- | An argument list for a function which of course is a list of paterns.
type FunctionPats = [HsPatP]

-- | A list of declarations used to represent a where or let clause.
type WhereDecls = [HsDeclP]

data PatFun = Mat | Patt | Er deriving (Eq, Show)

genFold args 
 = do
     let fileName = ghead "fileName'" args 
         beginRow         = read (args!!1)::Int
         beginCol         = read (args!!2)::Int
         endRow           = read (args!!3)::Int
         endCol           = read (args!!4)::Int
     AbstractIO.putStrLn "genFold" 
     
     modName <-fileNameToModName fileName
     let modName1 = convertModName modName
     (inscps, exps, mod, tokList)<-parseSourceFile fileName
     let subExp = locToExp (beginRow, beginCol) (endRow, endCol) tokList mod
     let (ty, pnt, pats, wh)
             = findDefNameAndExp tokList
                                 (beginRow, beginCol)
                                 (endRow, endCol)
                                 mod
          
     ((_,m), (newToks, (newMod, e))) <- applyRefac (addCommentDec pnt) (Just (inscps, exps, mod, tokList)) fileName
          
          
          
     writeRefactoredFiles False [((fileName, m), (newToks, newMod))]
          
     (inscps', exps', mod', tokList') <- parseSourceFile fileName
     
     
     ((_,m'), (newToks', (newMod',parsed))) <- applyRefac (retrieveDecAndRemove pnt e) 
                                                           (Just (inscps', exps', mod', tokList')) fileName
               
     let pnt' = declToPNT parsed          
     -- undo the added entry!                                         
     undoLatestInHistory
     (inscps2, exps2, mod2, tokList2)<-parseSourceFile fileName
     ((_,m'''), (newToks''', newMod''')) <- applyRefac (changeExpression pnt pnt' parsed subExp) 
                                                        (Just (inscps2, exps2, mod2, tokList2)) fileName
     writeRefactoredFiles False  [((fileName, m'''), (newToks''', newMod'''))]
     
     
     
     AbstractIO.putStrLn "Completed.\n"

addCommentDec pnt (_,_,t)
 = do
      tks <- extractComment (pNTtoPN pnt) t
      let tksString = concatMap getName tks
      let commentDef = dropWhile (==' ') (parseComment (reverse tksString))
      if '=' `elem` commentDef
       then do  
         mod <- insertTerm (dropWhile (==' ') (reverse commentDef)) (pNTtoPN pnt) t
         return (mod, (dropWhile (==' ') (reverse commentDef)))
       else do
         error "Please define an equation within a comment above the definition under scrutiny."

retrieveDecAndRemove pnt e (_,_,t)
 = do
      let parsedDec = retrieveDec pnt e t
      -- mod <- removeDec e t
      return (t, parsedDec)

retrieveDec pnt exp t
  = fromMaybe (error ("Previous record of definition not found! " ++ exp))
              (applyTU (once_tdTU (failTU `adhocTU` inDec)) t)
      where

      
        inDec d@(Dec (HsPatBind _ _ _ _))
          | (render.ppi) d == exp 
              = Just d
        inDec d@(Dec (HsFunBind l matches))
          | rendered matches exp
               = do
                  let x = rendered2 matches exp
                  Just (Dec (HsFunBind l [x]))
        inDec d = Nothing 

        rendered [] _ = False
        rendered (m:ms) e
          | rmLine ( rmToks ((render.ppi) m)) == rmLine ( rmToks e ) = True
          | otherwise           = rendered ms e
          
        rendered2 [] _ = error "Error in rendered2"
        rendered2 (m:ms) e 
          | rmLine ( rmToks ((render.ppi) m)) == rmLine ( rmToks e ) = m
          | otherwise           = rendered2 ms e

        rmToks x = (rmRBracket.rmLBracket.rmLine.rmSpace) x

        rmSpace x = filter (/=' ') x
        rmLine x = filter (/='\n') x
        rmLBracket x = filter (/='(') x
        rmRBracket x = filter (/=')') x
removeDec exp t
  = applyTP (stop_tdTP (idTP `adhocTP` inDec)) t
      where
        inDec (d::HsDeclP)
          | (render.ppi) d == exp 
              = do
                   rmDecl (declToPName2 d) True [d] 
                   return d 
        inDec d = return d


     

parseComment :: String -> String
parseComment [] = []
parseComment ('}':('-':xs)) = parseComment' xs
parseComment (x:xs) = parseComment xs

parseComment' :: String -> String
parseComment' [] = []
parseComment' ('-':('{':xs)) = ""
parseComment' (x:xs) = x : (parseComment' xs)

getName :: PosToken -> String
getName (_, ((Pos _ _ _), s)) = s 

changeExpression pnt pntComment d exp (_,_,t)
 = do 
      mod <- changeExpression' pnt pntComment d exp t
      return mod     
      
changeExpression' pnt pntComment d exp t
  = applyTP (stop_tdTP (failTP `adhocTP` (inExp d exp))) t
      where
        inExp (Dec (HsPatBind _ p (HsBody e2) ds)) exp (e::HsExpP)
          | sameOccurrence exp e 
              = do
                  if rmAllLocs e2 == rmAllLocs e
                    then do   
                      res <- update e (nameToExp (pNTtoName (patToPNT p))) e
                      return res
                    else do
                      mzero
        inExp (dec@(Dec (HsFunBind s matches))::HsDeclP) exp (e::HsExpP)
          | sameOccurrence exp e
                = do
                      let match@(HsMatch loc name pats (HsBody e2) ds) = getMatch pntComment matches
                      let newExp = searchForRHS pnt exp t
                      let (pred, newParams) = rewriteExp pats newExp e2
                      if pred
                         then do  
                          -- let patsConverd = map (render.ppi) newPats
                          res <- update e (createFunc pntComment (map patToExp newParams)) e
                          return res
                         else do
                          mzero
        inExp d exp e = mzero

rewriteExp :: [HsPatP] -> HsExpP -> HsExpP -> (Bool, [HsPatP])
rewriteExp pats (Exp (HsParen e)) e2 = rewriteExp pats e e2
rewriteExp pats e (Exp (HsParen e2)) = rewriteExp pats e e2
rewriteExp pats (Exp (HsLit s x)) (Exp (HsLit s1 x2))
 | x == x2 = (True, pats)
rewriteExp pats (Exp (HsId (HsCon x))) (Exp (HsId (HsCon y)))
 | x == y = (True, pats) 
rewriteExp pats n m@(Exp (HsId (HsVar i2)))
 | checkPNTInPat pats i2 = (True, getPatFromPats pats n i2)
rewriteExp pats n@(Exp (HsId (HsVar i1))) (Exp (HsId (HsVar i2)))
 | (pNTtoName i1) /= (pNTtoName i2) && checkPNTInPat pats i2 = (True, getPatFromPats pats n i2)
 | (pNTtoName i1) == (pNTtoName i2)  = (True, pats)
 | isLocalPNT i1 && isLocalPNT i2 = (True, pats)
 | otherwise = (False, pats)
rewriteExp pats a@(Exp (HsApp e1 e2)) b@(Exp (HsApp e3 e4))
 | pred1 == True && pred2 == True = (True, pats2)
   where
    (pred1, pats1) = rewriteExp pats  e1 e3
    (pred2, pats2) = rewriteExp pats1 e2 e4
rewriteExp pats a@(Exp (HsInfixApp e1 o1 e2)) b@(Exp (HsInfixApp e3 o2 e4))
 | o1 == o2 && (pred1 && pred2) = (True, pats2)
     where
       (pred1, pats1) = rewriteExp pats e1 e3
       (pred2, pats2) = rewriteExp pats1 e2 e4 
rewriteExp pats a@(Exp (HsTuple e1)) b@(Exp (HsTuple e2))
 | length e1 == length e2 && pred = (True, pats2)
 | otherwise = (False, pats)
    where
      (pred, pats2) = checkList pats e1 e2
rewriteExp pats a@(Exp (HsList e1)) b@(Exp (HsList e2))
 | length e1 == length e2 = (True, pats2)
 | otherwise = (False, pats)
    where
      (pred, pats2) = checkList pats e1 e2      
rewriteExp pats a@(Exp (HsLeftSection e1 i1)) b@(Exp (HsLeftSection e2 i2))
 | i1 == i2 && pred1 = (True, pats1)
     where
       (pred1, pats1) = rewriteExp pats e1 e2  
rewriteExp pats a@(Exp (HsRightSection i1 e1)) b@(Exp (HsRightSection i2 e2))
 | i1 == i2 && pred1 = (True, pats1)
     where
       (pred1, pats1) = rewriteExp pats e1 e2  
rewriteExp pats a@(Exp (HsEnumFrom e1)) b@(Exp (HsEnumFrom e2))
 | pred1 = (True, pats1)
   where
     (pred1, pats1) = rewriteExp pats e1 e2
rewriteExp pats a@(Exp (HsEnumFromTo e1 e2)) b@(Exp (HsEnumFromTo e3 e4))
 | pred1 && pred2 = (True, pats2)
    where
       (pred1, pats1) = rewriteExp pats e1 e3
       (pred2, pats2) = rewriteExp pats1 e2 e4 
rewriteExp pats a@(Exp (HsEnumFromThen e1 e2)) b2@(Exp (HsEnumFromThen e3 e4))
 | pred1 && pred2 = (True, pats2)
    where
       (pred1, pats1) = rewriteExp pats e1 e3
       (pred2, pats2) = rewriteExp pats1 e2 e4 
rewriteExp pats a@(Exp (HsEnumFromThenTo e1 e2 e3)) b@(Exp (HsEnumFromThenTo e4 e5 e6))
 | pred1 && pred2 && pred3 = (True, pats3)
    where
      (pred1, pats1) = rewriteExp pats e1 e4
      (pred2, pats2) = rewriteExp pats1 e2 e5
      (pred3, pats3) = rewriteExp pats2 e3 e6
rewriteExp pats a@(Exp (HsIf e1 e2 e3)) b@(Exp (HsIf e4 e5 e6))
 | pred1 && pred2 && pred3 = (True, pats3)
    where
      (pred1, pats1) = rewriteExp pats e1 e4
      (pred2, pats2) = rewriteExp pats1 e2 e5
      (pred3, pats3) = rewriteExp pats2 e3 e6
rewriteExp pats a@(Exp (HsNegApp s e)) b@(Exp (HsNegApp s2 e2))
 | pred = (True, pats1)
   where
     (pred, pats1) = rewriteExp pats e e2
rewriteExp pats a@(Exp (HsListComp stmts1)) b@(Exp (HsListComp stmts2))
 | pred = (True, pats1)
   where
     (pred, pats1) = rewriteExpStmts pats stmts1 stmts2
rewriteExp pats (Exp (HsDo stmts1)) (Exp (HsDo stmts2))
 | pred = (True, pats1)
    where
      (pred, pats1) = rewriteExpStmts pats stmts1 stmts2
rewriteExp pats a@(Exp (HsLambda ps e1)) b@(Exp (HsLambda ps2 e2))
 | wildCardAllPNs ps == wildCardAllPNs ps2 && pred = (True, pats1)
   where
     (pred, pats1) = rewriteExp pats e1 (localRewriteExp e2 (localRewritePats ps ps2))
     localRewritePats [] ps = []
     localRewritePats ps [] = []
     localRewritePats (p1:p1s) (p2:p2s) 
        = (rewritePat p2 p1) : (localRewritePats p1s p2s)
        
     localRewriteExp e [] = e   
     localRewriteExp e (p1:p1s)
       = let e1' = rewritePatsInExp p1 e  in localRewriteExp e1' p1s
       
rewriteExp pats a@(Exp (HsRecConstr s i fields1)) b@(Exp (HsRecConstr s2 i2 fields2))
 | i == i2 && pred = (True, pats1)
    where
      (pred, pats1) = rewriteExpFields pats fields1 fields2
rewriteExp pats a@(Exp (HsRecUpdate s1 e1 fields1)) b@(Exp (HsRecUpdate s2 e2 fields2))
 | pred1 && pred2 = (True, pats2)
   where
     (pred1, pats1) = rewriteExp pats e1 e2
     (pred2, pats2) = rewriteExpFields pats1 fields1 fields2
rewriteExp pats a@(Exp (HsCase e1 alts1)) b@(Exp (HsCase e2 alts2))
  | pred1 && pred2 = (True, pats2)
    where
      (pred1, pats1) = rewriteExp pats e1 e2
      (pred2, pats2) = rewriteExpAlts pats1 alts1 alts2

-- rewriteExp pats e1 e2 = error $ ">" ++ (show (pats, e1, e2))
rewriteExp pats e1 e2 = (False, pats) --  error $ ">" ++ (show (pats, e1, e2))

rewriteExpFields pats [] _ = (True, pats)
rewriteExpFields pats _ [] = (True, pats)
rewriteExpFields pats ((HsField i1 e1):f1) ((HsField i2 e2):f2)
  | pred && (i1 == i2 && pred2) = (True, pats2)
  | otherwise = (False, pats)
     where
       (pred, pats1) = rewriteExp pats e1 e2 
       (pred2, pats2) = rewriteExpFields pats1 f1 f2
  
rewriteExpAlts pats [] _ = (True, pats)
rewriteExpAlts pats _ [] = (True, pats)
rewriteExpAlts pats ((HsAlt _ p1 (HsBody e1) ds1):alts1) ((HsAlt _ p2 (HsBody e2) ds2):alts2)
  | pred && (wildCardAllPNs p1 == wildCardAllPNs p2 && pred2) = (True, pats2)
  | otherwise = (False, pats)
     where
       (pred, pats1) = rewriteExp pats e1 (rewritePatsInExp (rewritePat p2 p1) e2)
       (pred2, pats2) = rewriteExpAlts pats1 alts1 alts2
rewriteExpAlts pats ((HsAlt _ p1 (HsGuard gs1) ds1):alts1) ((HsAlt _ p2 (HsGuard gs2) ds2):alts2)
  | (wildCardAllPNs p1 == wildCardAllPNs p2 && pred2) = (True, pats2)
  | otherwise = (False, pats)
     where
       -- (pred, pats1) = rewriteExp pats e1 e2 
       (pred2, pats2) = rewriteExpGuards pats gs1 res
       res =  (rewritePatsInGuard (rewritePat p2 p1) gs2)
rewriteExpAlts pats _ _ = (False, pats)

rewriteExpGuards pats [] _ = (True, pats)
rewriteExpGuards pats _ [] = (True, pats)
rewriteExpGuards pats ((_ ,e1,e2):gs1) ((_, e3, e4):gs2)    
  | pred1 && pred2 && pred3 = (True, pats3)
    where
     (pred1, pats1) = rewriteExp pats e1 e3
     (pred2, pats2) = rewriteExp pats1 e2 e4
     (pred3, pats3) = rewriteExpGuards pats2 gs1 gs2     
       
       
rewriteExpStmts pats s1@(HsGenerator _ p1 e1 m1) s2@(HsGenerator _ p2 e2 m2)
 | wildCardAllPNs p1 == wildCardAllPNs p2 && pred1 && pred2 = (True, pats2)
  where
   (pred1, pats1) =  rewriteExp pats e1 e2 
   (pred2, pats2) = rewriteExpStmts pats1 m1 m2
rewriteExpStmts pats (HsLast e1) (HsLast e2)
 | wildCardAllPNs e1 == wildCardAllPNs e2 = (True, pats)
 -- = error $ show $ rewriteExp pats e1 e2
rewriteExpStmts pats (HsQualifier e1 stmts) (HsQualifier e2 stmts2)
 | pred1 && pred2 = (True, pats2)
  where
   (pred1, pats1) =  rewriteExp pats e1 e2 
   (pred2, pats2) = rewriteExpStmts pats1 stmts stmts2
rewriteExpStmts pats (HsLetStmt ds1 stmts) (HsLetStmt ds2 stmts2)
 = rewriteExpStmts pats stmts stmts2
rewriteExpStmts pats s1 s2 = (False, pats)

changeAllNames :: PNT -> PNT -> PNT 
changeAllNames pnt  t =runIdentity (applyTP (full_buTP (idTP `adhocTP` l)) t) 
   where 
         l ((PN (UnQual n) s)) 
           | n /= (pNTtoName pnt) =  return ((PN (UnQual (pNTtoName pnt)) s))
           | otherwise = return ((PN (UnQual n) s))

rewritePat :: HsPatP -> HsPatP -> HsPatP
rewritePat a@(Pat (HsPId (HsVar x))) b@(Pat (HsPId (HsVar y)))
  = (Pat (HsPId (HsVar (changeAllNames y x))))
rewritePat a@(Pat (HsPId (HsCon x))) b@(Pat (HsPId (HsCon y)))
  = a
rewritePat a@(Pat (HsPLit _ _)) b@(Pat (HsPLit _ _))
  = a
rewritePat a@(Pat (HsPNeg _ _)) b@(Pat (HsPNeg _ _)) 
  = a
rewritePat a@(Pat (HsPSucc _ _ _)) b@(Pat (HsPSucc _ _ _))
  = a
rewritePat a@(Pat (HsPInfixApp p1 i p2)) b@(Pat (HsPInfixApp p3 i2 p4))
  = (Pat (HsPInfixApp (rewritePat p1 p3) i (rewritePat p2 p4)))
rewritePat a@(Pat (HsPApp i ps)) b@(Pat (HsPApp i2 ps2))
  = (Pat (HsPApp i (rewritePat' ps ps2)))
rewritePat a@(Pat (HsPTuple s ps)) b@(Pat (HsPTuple s2 ps2))
  = (Pat (HsPTuple s (rewritePat' ps ps2)))
rewritePat a@(Pat (HsPList s ps)) b@(Pat (HsPList s2 ps2))
  = (Pat (HsPList s (rewritePat' ps ps2)))
rewritePat a@(Pat (HsPParen p1)) b@(Pat (HsPParen p2))
  = (Pat (HsPParen (rewritePat p1 p2)))
rewritePat a@(Pat (HsPAsPat i p1)) b@(Pat (HsPAsPat i2 p2))
  = (Pat (HsPAsPat i (rewritePat p1 p2)))
rewritePat a@(Pat (HsPWildCard)) b@(Pat (HsPWildCard))
 = a
rewritePat a@(Pat (HsPIrrPat p1)) b@(Pat (HsPIrrPat p2))
 = Pat (HsPIrrPat (rewritePat p1 p2))
rewritePat a b = a

rewritePat' :: [HsPatP] -> [HsPatP] -> [HsPatP]
rewritePat' [] _ = []
rewritePat' x [] = x
rewritePat' (x:xs) (y:ys)
  = rewritePat x y : rewritePat' xs ys
 
 
rewritePatsInExp :: HsPatP -> HsExpP -> HsExpP
rewritePatsInExp p a@(Exp (HsId (HsVar x))) 
 | isTopLevelPNT x = a
 | otherwise
   = (Exp (HsId (HsVar newPNT)))
     where
       newPNT = grabPNT x (hsPNTs p)
rewritePatsInExp p (Exp (HsInfixApp e1 o e2))
  = (Exp (HsInfixApp e1' o e2'))
      where
        e1' = rewritePatsInExp p e1
        e2' = rewritePatsInExp p e2
rewritePatsInExp p (Exp (HsApp e1 e2)) 
  = (Exp (HsApp e1' e2'))
      where
        e1' = rewritePatsInExp p e1
        e2' = rewritePatsInExp p e2
rewritePatsInExp p (Exp (HsNegApp s e))
  = (Exp (HsNegApp s e'))
     where
       e' = rewritePatsInExp p e
rewritePatsInExp p (Exp (HsLambda ps e))
  = (Exp (HsLambda ps e'))
     where
       e' = rewritePatsInExp p e
rewritePatsInExp p (Exp (HsLet ds e))
  = (Exp (HsLet ds e'))
     where
       e' = rewritePatsInExp p e
rewritePatsInExp p (Exp (HsIf e1 e2 e3))
  = (Exp (HsIf e1' e2' e3'))
     where
       e1'  = rewritePatsInExp p e1
       e2'  = rewritePatsInExp p e2
       e3'  = rewritePatsInExp p e3
rewritePatsInExp p (Exp (HsCase e alts))
  = (Exp (HsCase e' alts'))
      where
        e' = rewritePatsInExp p e
        alts' = rewritePatsInAlts p alts
rewritePatsInExp p (Exp (HsDo stmts))
  = (Exp (HsDo stmts'))
     where
       stmts' = rewritePatsInStmts p stmts
rewritePatsInExp p (Exp (HsTuple es))
  = (Exp (HsTuple es'))
     where
      es' = map (rewritePatsInExp p) es        
rewritePatsInExp p (Exp (HsList es))
  = (Exp (HsList es'))
     where
       es' = map (rewritePatsInExp p) es
rewritePatsInExp p (Exp (HsParen e))
  = (Exp (HsParen (rewritePatsInExp p e)))
rewritePatsInExp p (Exp (HsLeftSection e (HsVar x)))
  = (Exp (HsLeftSection e' (HsVar newPNT)))
     where
       e' = rewritePatsInExp p e'
       newPNT = grabPNT x (hsPNTs p)
rewritePatsInExp p (Exp (HsRightSection (HsVar x) e))
  = (Exp (HsRightSection (HsVar newPNT) e'))
     where
       e' = rewritePatsInExp p e'
       newPNT = grabPNT x (hsPNTs p)
rewritePatsInExp p (Exp (HsRecConstr s i fields))
  = (Exp (HsRecConstr s newPNT fields'))
      where
        newPNT = grabPNT i (hsPNTs p)
        fields' = rewritePatsInFields p fields
rewritePatsInExp p (Exp (HsRecUpdate s e fields))
  = (Exp (HsRecUpdate s e' fields'))
      where
       e' = rewritePatsInExp p e
       fields' = rewritePatsInFields p fields
rewritePatsInExp p (Exp (HsEnumFrom e))
 = (Exp (HsEnumFrom e'))
    where
     e' = rewritePatsInExp p e
rewritePatsInExp p (Exp (HsEnumFromTo e1 e2))
 = (Exp (HsEnumFromTo e1' e2'))
    where
      e1' = rewritePatsInExp p e1
      e2' = rewritePatsInExp p e2
rewritePatsInExp p (Exp (HsEnumFromThen e1 e2))
 = (Exp (HsEnumFromThen e1' e2'))
     where
       e1' = rewritePatsInExp p e1
       e2' = rewritePatsInExp p e2
rewritePatsInExp p (Exp (HsEnumFromThenTo e1 e2 e3))
 = (Exp (HsEnumFromThenTo e1' e2' e3'))
     where
       e1' = rewritePatsInExp p e1
       e2' = rewritePatsInExp p e2
       e3' = rewritePatsInExp p e3
rewritePatsInExp p (Exp (HsListComp stmts))
 = (Exp (HsListComp stmts'))
     where
       stmts' = rewritePatsInStmts p stmts
rewritePatsInExp p e = e

rewritePatsInStmts :: HsPatP -> HsStmtP -> HsStmtP
rewritePatsInStmts p (HsGenerator s p2 e stmts)
 = (HsGenerator s p2 e' stmts')
   where
     e' = rewritePatsInExp p e
     stmts' = rewritePatsInStmts p stmts
rewritePatsInStmts p (HsQualifier e stmts)
 = (HsQualifier e' stmts')
    where
     e' = rewritePatsInExp p e
     stmts' = rewritePatsInStmts p stmts
rewritePatsInStmts p (HsLetStmt ds stmts)
 = (HsLetStmt ds stmts')
    where
     stmts' = rewritePatsInStmts p stmts
rewritePatsInStmts p (HsLast e)
 = (HsLast e')
    where
      e' = rewritePatsInExp p e

rewritePatsInFields :: HsPatP -> [HsFieldP] -> [HsFieldP]
rewritePatsInFields p [] = []
rewritePatsInFields p ((HsField i e):fs)
 = (HsField newPNT e') : (rewritePatsInFields p fs)
    where
      newPNT = grabPNT i (hsPNTs e)
      e' = rewritePatsInExp p e

rewritePatsInAlts :: HsPatP -> [HsAltP] -> [HsAltP]
rewritePatsInAlts p [] = []
rewritePatsInAlts p ((HsAlt s p2 (HsBody e) ds):alts)
 = (HsAlt s p2 (HsBody e') ds) : (rewritePatsInAlts p alts)
     where
       e' = rewritePatsInExp p e
rewritePatsInAlts p ((HsAlt s p2 (HsGuard gs) ds):alts)
 = (HsAlt s p2 (HsGuard gs') ds) : (rewritePatsInAlts p alts)
     where
       gs' = rewritePatsInGuard p gs

rewritePatsInGuard :: HsPatP -> [(SrcLoc, HsExpP, HsExpP)] -> [(SrcLoc, HsExpP, HsExpP)]
rewritePatsInGuard p [] = []
rewritePatsInGuard p ((s, e1, e2):gs)
 = (s, e1', e2') : (rewritePatsInGuard p gs)
    where
      e1' = rewritePatsInExp p e1
      e2' = rewritePatsInExp p e2


grabPNT :: PNT -> [PNT] -> PNT
grabPNT x [] = x
grabPNT x (y:ys)
  | defineLoc x == defineLoc y = y
  | otherwise = grabPNT x ys


checkList :: [HsPatP] -> [HsExpP] -> [HsExpP] -> (Bool, [HsPatP])
checkList pats [] [] = (True, pats)
checkList pats [e] [l] = rewriteExp pats e l
checkList pats (e:es) (l:ls) 
        | pred = checkList pats' es ls
        | otherwise = (False, pats')
           where
             (pred, pats') = rewriteExp pats e l
checkPNTInPat :: [HsPatP] -> PNT -> Bool
checkPNTInPat [] _ = False
checkPNTInPat (p:ps) i 
        | defineLoc i == (SrcLoc "__unknown__" 0 0 0) = False
        | defineLoc i == defineLoc (patToPNT p) = True
        | otherwise = checkPNTInPat ps i


getPatFromPats :: [HsPatP] -> HsExpP -> PNT -> [HsPatP]
getPatFromPats [] _ _ = []
getPatFromPats (p:pats) e i2
     | defineLoc i2 == (SrcLoc "__unknown__" 0 0 0) = p : (getPatFromPats pats e i2)
     | defineLoc i2 == defineLoc (patToPNT p) = (expToPat e) ++ (getPatFromPats pats e i2)
     | otherwise = p : (getPatFromPats pats e i2)

patToExp :: HsPatP -> HsExpP
patToExp (Pat (HsPId x)) = (Exp (HsId x))
patToExp (Pat (HsPLit s l)) = (Exp (HsLit s l))
patToExp (Pat (HsPInfixApp p1 i p2)) = (Exp (HsInfixApp (patToExp p1) (HsCon i) (patToExp p2)))
patToExp (Pat (HsPApp pnt p2)) = (Exp (HsApp (nameToExp (pNTtoName pnt)) (cApp p2)))
                                    where
                                      cApp :: [HsPatP] -> HsExpP
                                      cApp [p] = patToExp p
                                      cApp (p:ps) = Exp (HsApp (cApp (init (p:ps))) (patToExp (last ps)))
patToExp (Pat (HsPTuple s ps)) = (Exp (HsTuple (map patToExp ps)))
patToExp (Pat (HsPList s ps)) = (Exp (HsList (map patToExp ps)))
patToExp (Pat (HsPParen p1)) = (Exp (HsParen (patToExp p1)))
patToExp (Pat (HsPAsPat pnt pat)) = (Exp (HsId (HsVar pnt)))
patToExp (Pat (HsPIrrPat p)) = patToExp p
patToExp (Pat (HsPWildCard)) = nameToExp "undefined"

expToPat :: HsExpP -> [HsPatP]
expToPat (Exp (HsId x)) = [Pat (HsPId x)]
expToPat (Exp (HsLit s l)) = [Pat (HsPLit s l)]
expToPat (Exp (HsInfixApp e1 (HsVar i) e2)) = [Pat (HsPInfixApp (ghead "expToPat" $ expToPat e1) 
                                                             i (ghead "expToPat" $ expToPat e2))]
expToPat (Exp (HsInfixApp e1 (HsCon i) e2)) = [Pat (HsPInfixApp (ghead "expToPat" $ expToPat e1) 
                                                             i (ghead "expToPat" $ expToPat e2))]
expToPat e@(Exp (HsApp e1 e2)) =    [Pat (HsPApp (nameToPNT " ")
                                                   (concatMap expToPat exps))]
                               where
                                 exps = flatternApp e         
expToPat (Exp (HsLambda ps e)) = ps
expToPat (Exp (HsTuple es)) = concatMap expToPat es   --[Pat (HsPTuple loc0 (concatMap expToPat es))]
expToPat (Exp (HsList es)) = concatMap expToPat es
expToPat (Exp (HsParen e1)) = [Pat (HsPParen (ghead "expToPat" $ expToPat e1))]
expToPat e = [] 

flatternApp :: HsExpP -> [HsExpP]
flatternApp (Exp (HsApp e1 e2)) = flatternApp e1 ++ flatternApp e2
flatternApp (Exp (HsParen e)) = flatternApp e
flatternApp x = [x]

searchForRHS pnt exp t
  = (fromMaybe (error "Could not find the right hand side for the commented definition!"))
    (applyTU (once_tdTU (failTU `adhocTU` (inDec exp))) t)
      where
        inDec e (pat@(Dec (HsPatBind loc1 ps rhs@(HsBody e2) ds))::HsDeclP)
         | findEntity e e2
            = do
               let newExp = symbolicallyTie exp e2 []
               Just newExp               
        
        inDec e (dec@(Dec (HsFunBind s matches))::HsDeclP)
         | findPNT pnt matches
          = do
               let match@(HsMatch loc name pats rhs ds) = getMatch pnt matches
                   
               if findEntity e (extractRHS e rhs)
                  then do
                         let newExp = symbolicallyTie exp (extractRHS e rhs) pats
                         Just newExp
                  else do
                         mzero
        inDec _ d = mzero

findGuard e [] = error "Cannot find highlight expression!"
findGuard e ((_,_,x):xs) 
  | findEntity e x = x
  | otherwise = findGuard e xs
extractRHS e (HsBody x) = x
extractRHS e (HsGuard gs) = findGuard e gs

symbolicallyTie exp (Exp (HsParen e)) pats = symbolicallyTie exp e pats 
symbolicallyTie exp n@(Exp (HsCase e alts)) pats
     = symbolicallyTie' alts  -- (Exp (HsCase e [symbolicallyTie' alts]))
        where
          symbolicallyTie' [] = error "Error in symbolic evaluation: expression does not occur on the RHS!" 
          symbolicallyTie' (a@(HsAlt l p (HsGuard gs) ds):as)
           | findEntity exp gs
            = zipPatExp e n a' pats
           | findEntity exp ds           -- selected definition is in where clause of a case alt.
            = zipPatExp e n a' pats
            where
              a' = (HsAlt l p (HsBody exp) ds)
          symbolicallyTie' (a@(HsAlt l p (HsBody e2) ds):as) 
           | findEntity exp e2
               = zipPatExp e n a' pats
           | findEntity exp ds           -- selected definition is in where clause of a case alt.
               = zipPatExp e n a' pats
           | otherwise = symbolicallyTie' as
            where
              a' = (HsAlt l p (HsBody exp) ds)
symbolicallyTie exp e pats = exp          
-- utility functions 
getMatch :: PNT -> [HsMatchP] -> HsMatchP   
getMatch _ [] = error "Please select a case in top-level expression scope!"
getMatch pnt (match@(HsMatch loc name pats rhs ds):ms)
  | useLoc pnt == useLoc name      = match
  | otherwise        = getMatch pnt ms 
     
convertModName (PlainModule s) = s
convertModName m@(MainModule f) = modNameToStr m 

{-|
Takes the position of the highlighted code and returns
the function name, the list of arguments, the expression that has been
highlighted by the user, and any where\/let clauses associated with the
function. 
-}

findDefNameAndExp :: Term t => [PosToken] -- ^ The token stream for the 
                                          -- file to be
                                          -- refactored.
                  -> (Int, Int) -- ^ The beginning position of the highlighting.
                  -> (Int, Int) -- ^ The end position of the highlighting.
                  -> t          -- ^ The abstract syntax tree.
                  -> (PatFun, PNT, FunctionPats, WhereDecls) -- ^ A tuple of,
                     -- (the function name, the list of arguments,
                     -- the expression highlighted, any where\/let clauses
                     -- associated with the function).
                     
findDefNameAndExp toks beginPos endPos t
  = fromMaybe (Er, defaultPNT, [], [])
              (applyTU (once_buTU (failTU `adhocTU` inMatch `adhocTU` inPat)) t)

    where
      --The selected sub-expression is in the rhs of a match
      inMatch (match@(HsMatch loc1  pnt pats rhs@(HsBody e) ds)::HsMatchP)
        | locToExp beginPos endPos toks rhs /= defaultExp
          = Just (Mat, pnt, pats, ds)
      inMatch (match@(HsMatch loc1  pnt pats rhs@(HsGuard e) ds)::HsMatchP)
        | locToExp beginPos endPos toks rhs /= defaultExp
          = Just (Mat, pnt, pats, ds)          
      inMatch _ = Nothing


      --The selected sub-expression is in the rhs of a pattern-binding
      inPat (pat@(Dec (HsPatBind loc1 ps rhs ds))::HsDeclP)
        | locToExp beginPos endPos toks rhs /= defaultExp
            = if isSimplePatBind pat
                 then Just (Patt, patToPNT ps, [], ds)    
                 else Just (Patt, pnt, pats, ds)
                    where 
                      (_, pnt, pats, ds') = findDefining pat t
                      findDefining pats t 
                        = fromMaybe (error "Error: the selected entity is a top level complex pattern binding!")
                                    (applyTU (once_buTU (failTU `adhocTU` inMatch')) t)
                                    
                      inMatch' (match@(HsMatch loc1  pnt pats rhs@(HsBody e) ds)::HsMatchP)
                         | pat `elem` ds = Just (Mat, pnt, pats, ds)
                         | findEntity pat e = Just (Mat, pnt, pats, ds)
                      inMatch' (match@(HsMatch loc1  pnt pats rhs@(HsGuard e) ds)::HsMatchP)
                         | pat `elem` ds = Just (Mat, pnt, pats, ds)
                         | findEntity pat e = Just (Mat, pnt, pats, ds)
                      inMatch' _ = Nothing
                    
      inPat _ = Nothing

