{-This refactoring moves a user-selected function definition/pattern binding from current module to a
  user specified module.
  To perform this refactoring: put the cursor at the begining of the function/pattern name, then select
  the 'moveDefBtwMod' menu from the 'refactor' menu, and input the target module name in the mini-buffer
  as prompted.-}

moveDefBtwMod args
 = do let fileName     = ghead "filename" args  
          destModName' = args!! 1      
          destModName  = strToModName destModName'  -- there may be problem with the main module
          row          = read (args!!2)::Int
          col          = read (args!!3)::Int
      origModName <- fileNameToModName fileName
      r <-isAnExistingMod destModName
      unless r $  error "The specified module does not exist!"  
      unless (origModName /= destModName) $ error "The target module name is the same as the current module name!"
      (origInscps, _, origMod, origToks) <- parseSourceFile fileName    
      let pn = pNTtoPN $ locToPNT fileName (row, col) origMod  
          decl =definingDecls [pn] (hsModDecls origMod) False True                                      
      unless (pn /= defaultPN) 
         $ error "Invalid cursor position. Please point the cursor to the beginning of the function/pattern name!"
      unless (isTopLevelPN pn && decl/=[]) $ error "The selected definition is not a top-level function/pattern definition!"
      -- Find the file name of the target module.
      destFile<-PFE0.findFile destModName  
      -- Parse the target module.
      (destInscps, _, destMod, destToks) <-parseSourceFile destFile 
      -- get the pnames defined by the declaration to be moved.
      let pns  = nub $ concatMap definedPNs decl
      -- get the subset of 'pns' which are already defined in the target module.
      namesDefined <- namesDefinedIn (map pNtoName pns)  destMod
      unless (namesDefined == [] )
           (if length namesDefined == 1 
               then error ("The function/pattern name: " ++ show (head (namesDefined))
                    ++ " is already defined in module "++ destModName'++ " !")
               else error ("The pattern names: " ++ show namesDefined ++ " are already defined in module " ++ destModName'++ " !"))
      -- Get the client modules and the corresponding file names of the original module.
      clientsOfOrigMod <- clientModsAndFiles =<< RefacUtils.fileNameToModName fileName 
      -- Get the client modules and the corresponding file names of the original module.
      clientsOfDestMod <- clientModsAndFiles =<< RefacUtils.fileNameToModName destFile 
      -- get the free variables in the definition to be moved.
      freePnts <- freePNTsIn pns decl            
      let pnsNotInscope = varsNotInScope freePnts destInscps                          
      unless (pnsNotInscope == [])   -- why can not import these variables in the dest module?
          (if length pnsNotInscope == 1
             then error ("The free variable: '" ++ pNtoName (head pnsNotInscope)++
                      "', which is used by the definition to be moved is not in scope in the target module "++destModName' ++ " !")
             else error ("The free variables: " ++ showEntities pNtoName pnsNotInscope ++
                      ", which are used by the definition to be moved are not in scope in the target module " ++destModName' ++ " !")) 
       -- Will the moving cause recursive modules?
      r <-causeRecursiveMods decl fileName origModName destModName    
      unless (not r) $ error ("Moving the definition to module " ++ show destModName++ " will cause mutually recursive modules!")
      -- Remove the definition from the original module.  
      (origMod', ((origToks',origM),_))<-rmCodeFromMod pns fileName (origMod, origToks) destModName destFile
      -- Parse all those client modules.
      let modsAndFiles=nub (clientsOfOrigMod++clientsOfDestMod) \\ [(origModName, fileName),(destModName, destFile)]
      parsedMods <-mapM parseSourceFile $ map snd modsAndFiles                 
      -- is the definition name used in modules other than the target module?
      let used = any (\(_,_,mod,_)->isUsedByMod pns mod) parsedMods  || isUsedByMod pns origMod
      -- Add the definition to the destination module.
      (destMod',((destToks',destM),_))<-addCodeToMod pns destFile (origMod, origToks)
                                        (destInscps,destModName,destMod, destToks) used
      --Do corrsponding modification in the client modules.
      refacClients <- mapM (refactorInClientMod origModName destModName pns) $ zip parsedMods $ map snd modsAndFiles 
      
      -- output the result.
      writeRefactoredFiles False $ [((fileName,origM),(origToks',origMod')),
                                    ((destFile,destM),(destToks',destMod'))] ++ refacClients


-- Return the subset of 'names' that are defined in 't'.
moveDefBtwMod args
 = do let fileName     = ghead "filename" args  
          destModName' = args!! 1      
          destModName  = strToModName destModName'  -- there may be problem with the main module
          row          = read (args!!2)::Int
          col          = read (args!!3)::Int
      origModName <- fileNameToModName fileName
      r <-isAnExistingMod destModName
      unless r $  error "The specified module does not exist!"  
      unless (origModName /= destModName) $ error "The target module name is the same as the current module name!"
      (origInscps, _, origMod, origToks) <- parseSourceFile fileName    
      let pn = pNTtoPN $ locToPNT fileName (row, col) origMod  
          decl =definingDecls [pn] (hsModDecls origMod) False True                                      
      unless (pn /= defaultPN) 
         $ error "Invalid cursor position. Please point the cursor to the beginning of the function/pattern name!"
      unless (isTopLevelPN pn && decl/=[]) $ error "The selected definition is not a top-level function/pattern definition!"
      -- Find the file name of the target module.
      destFile<-PFE0.findFile destModName  
      -- Parse the target module.
      (destInscps, _, destMod, destToks) <-parseSourceFile destFile 
      -- get the pnames defined by the declaration to be moved.
      let pns  = nub $ concatMap definedPNs decl
      -- get the subset of 'pns' which are already defined in the target module.
      namesDefined <- namesDefinedIn (map pNtoName pns)  destMod
      unless (namesDefined == [] )
           (if length namesDefined == 1 
               then error ("The function/pattern name: " ++ show (head (namesDefined))
                    ++ " is already defined in module "++ destModName'++ " !")
               else error ("The pattern names: " ++ show namesDefined ++ " are already defined in module " ++ destModName'++ " !"))
      -- Get the client modules and the corresponding file names of the original module.
      clientsOfOrigMod <- clientModsAndFiles =<< RefacUtils.fileNameToModName fileName 
      -- Get the client modules and the corresponding file names of the original module.
      clientsOfDestMod <- clientModsAndFiles =<< RefacUtils.fileNameToModName destFile 
      -- get the free variables in the definition to be moved.
      freePnts <- freePNTsIn pns decl            
      let pnsNotInscope = varsNotInScope freePnts destInscps                          
      unless (pnsNotInscope == [])   -- why can not import these variables in the dest module?
          (if length pnsNotInscope == 1
             then error ("The free variable: '" ++ pNtoName (head pnsNotInscope)++
                      "', which is used by the definition to be moved is not in scope in the target module "++destModName' ++ " !")
             else error ("The free variables: " ++ showEntities pNtoName pnsNotInscope ++
                      ", which are used by the definition to be moved are not in scope in the target module " ++destModName' ++ " !")) 
       -- Will the moving cause recursive modules?
      r <-causeRecursiveMods decl fileName origModName destModName    
      unless (not r) $ error ("Moving the definition to module " ++ show destModName++ " will cause mutually recursive modules!")
      -- Remove the definition from the original module.  
      (origMod', ((origToks',origM),_))<-rmCodeFromMod pns fileName (origMod, origToks) destModName destFile
      -- Parse all those client modules.
      let modsAndFiles=nub (clientsOfOrigMod++clientsOfDestMod) \\ [(origModName, fileName),(destModName, destFile)]
      parsedMods <-mapM parseSourceFile $ map snd modsAndFiles                 
      -- is the definition name used in modules other than the target module?
      let used = any (\(_,_,mod,_)->isUsedByMod pns mod) parsedMods  || isUsedByMod pns origMod
      -- Add the definition to the destination module.
      (destMod',((destToks',destM),_))<-addCodeToMod pns destFile (origMod, origToks)
                                        (destInscps,destModName,destMod, destToks) used
      --Do corrsponding modification in the client modules.
      refacClients <- mapM (refactorInClientMod origModName destModName pns) $ zip parsedMods $ map snd modsAndFiles 
      
      -- output the result.
      writeRefactoredFiles False $ [((fileName,origM),(origToks',origMod')),
                                    ((destFile,destM),(destToks',destMod'))] ++ refacClients


-- Return the subset of 'names' that are defined in 't'.
moveDefBtwMod args
 = do let fileName     = ghead "filename" args  
          destModName' = args!! 1      
          destModName  = strToModName destModName'  -- there may be problem with the main module
          row          = read (args!!2)::Int
          col          = read (args!!3)::Int
      origModName <- fileNameToModName fileName
      r <-isAnExistingMod destModName
      unless r $  error "The specified module does not exist!"  
      unless (origModName /= destModName) $ error "The target module name is the same as the current module name!"
      (origInscps, _, origMod, origToks) <- parseSourceFile fileName    
      let pn = pNTtoPN $ locToPNT fileName (row, col) origMod  
          decl =definingDecls [pn] (hsModDecls origMod) False True                                      
      unless (pn /= defaultPN) 
         $ error "Invalid cursor position. Please point the cursor to the beginning of the function/pattern name!"
      unless (isTopLevelPN pn && decl/=[]) $ error "The selected definition is not a top-level function/pattern definition!"
      -- Find the file name of the target module.
      destFile<-PFE0.findFile destModName  
      -- Parse the target module.
      (destInscps, _, destMod, destToks) <-parseSourceFile destFile 
      -- get the pnames defined by the declaration to be moved.
      let pns  = nub $ concatMap definedPNs decl
      -- get the subset of 'pns' which are already defined in the target module.
      namesDefined <- namesDefinedIn (map pNtoName pns)  destMod
      unless (namesDefined == [] )
           (if length namesDefined == 1 
               then error ("The function/pattern name: " ++ show (head (namesDefined))
                    ++ " is already defined in module "++ destModName'++ " !")
               else error ("The pattern names: " ++ show namesDefined ++ " are already defined in module " ++ destModName'++ " !"))
      -- Get the client modules and the corresponding file names of the original module.
      clientsOfOrigMod <- clientModsAndFiles =<< RefacUtils.fileNameToModName fileName 
      -- Get the client modules and the corresponding file names of the original module.
      clientsOfDestMod <- clientModsAndFiles =<< RefacUtils.fileNameToModName destFile 
      -- get the free variables in the definition to be moved.
      freePnts <- freePNTsIn pns decl            
      let pnsNotInscope = varsNotInScope freePnts destInscps                          
      unless (pnsNotInscope == [])   -- why can not import these variables in the dest module?
          (if length pnsNotInscope == 1
             then error ("The free variable: '" ++ pNtoName (head pnsNotInscope)++
                      "', which is used by the definition to be moved is not in scope in the target module "++destModName' ++ " !")
             else error ("The free variables: " ++ showEntities pNtoName pnsNotInscope ++
                      ", which are used by the definition to be moved are not in scope in the target module " ++destModName' ++ " !")) 
       -- Will the moving cause recursive modules?
      r <-causeRecursiveMods decl fileName origModName destModName    
      unless (not r) $ error ("Moving the definition to module " ++ show destModName++ " will cause mutually recursive modules!")
      -- Remove the definition from the original module.  
      (origMod', ((origToks',origM),_))<-rmCodeFromMod pns fileName (origMod, origToks) destModName destFile
      -- Parse all those client modules.
      let modsAndFiles=nub (clientsOfOrigMod++clientsOfDestMod) \\ [(origModName, fileName),(destModName, destFile)]
      parsedMods <-mapM parseSourceFile $ map snd modsAndFiles                 
      -- is the definition name used in modules other than the target module?
      let used = any (\(_,_,mod,_)->isUsedByMod pns mod) parsedMods  || isUsedByMod pns origMod
      -- Add the definition to the destination module.
      (destMod',((destToks',destM),_))<-addCodeToMod pns destFile (origMod, origToks)
                                        (destInscps,destModName,destMod, destToks) used
      --Do corrsponding modification in the client modules.
      refacClients <- mapM (refactorInClientMod origModName destModName pns) $ zip parsedMods $ map snd modsAndFiles 
      
      -- output the result.
      writeRefactoredFiles False $ [((fileName,origM),(origToks',origMod')),
                                    ((destFile,destM),(destToks',destMod'))] ++ refacClients


-- Return the subset of 'names' that are defined in 't'.