package com.chrisfolger.needsmoredojo.core.refactoring;

import com.chrisfolger.needsmoredojo.core.amd.DeclareFinder;
import com.chrisfolger.needsmoredojo.core.amd.ImportCreator;
import com.chrisfolger.needsmoredojo.core.amd.SourceLibrary;
import com.chrisfolger.needsmoredojo.core.util.DefineStatement;
import com.intellij.lang.javascript.psi.JSExpression;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.search.FilenameIndex;
import com.intellij.psi.search.GlobalSearchScope;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;

/**
 * this is made to find modules that reference another dojo module
 */
public class ModuleRenamer
{
    private PsiFile[] possibleFiles;
    private SourceLibrary[] libraries;
    private PsiFile moduleFile;
    private Project project;
    private String moduleName;

    public ModuleRenamer(PsiFile[] possibleImportFiles, String moduleName, PsiFile moduleFile, SourceLibrary[] libraries)
    {
        this.moduleName = moduleName;
        this.moduleFile = moduleFile;
        this.project = moduleFile.getProject();
        this.libraries = libraries;
        this.possibleFiles = possibleImportFiles;
    }

    protected void getMatch(String newModuleName, DefineStatement statement, PsiFile targetFile)
    {
        // smoke test
        if(!statement.getArguments().getText().contains(moduleName))
        {
            return;
        }

        // get a list of possible modules and their syntax
        LinkedHashMap<String, PsiFile> results = new ImportCreator().getChoicesFromFiles(possibleFiles, libraries, moduleName, targetFile, false, true);

        // go through the defines and determine if there is a match
        int matchIndex = -1;
        for(int i=0;i<statement.getArguments().getExpressions().length;i++)
        {
            JSExpression argument = statement.getArguments().getExpressions()[i];

            String argumentText = argument.getText().replaceAll("'", "").replace("\"", "");
            if(argumentText.contains(moduleName))
            {
                StringBuilder b = new StringBuilder(argumentText);
                b.replace(argumentText.lastIndexOf(moduleName), argumentText.lastIndexOf(moduleName) + moduleName.length(), newModuleName );
                argumentText = b.toString();
            }

            if(results.containsKey(argumentText))
            {
                matchIndex = i;
                break;
            }
        }

        int i=0;
    }

    public PsiFile[] findFilesThatReferenceModule(VirtualFile[] projectSourceDirectories)
    {
        List<VirtualFile> directories = new ArrayList<VirtualFile>();
        for(VirtualFile file : projectSourceDirectories)
        {
            directories.add(file);
        }

        DeclareFinder finder = new DeclareFinder();
        PsiManager psiManager = PsiManager.getInstance(project);

        // TODO can we use a directory scope instead???
        Collection<VirtualFile> results = FilenameIndex.getAllFilesByExt(project, "js", GlobalSearchScope.projectScope(project));

        for(VirtualFile file : results)
        {
            boolean isInProjectDirectory = VfsUtil.isAncestor(projectSourceDirectories[0], file, true);
            if(!isInProjectDirectory) continue;

            PsiFile psiFile = psiManager.findFile(file);
            if(!psiFile.getText().contains("define("))
            {
                continue;
            }

            DefineStatement defineStatement = finder.getDefineStatementItems(psiFile);
            getMatch(moduleFile.getName().substring(0, moduleFile.getName().indexOf('.')), defineStatement, psiFile);
        }
        return null;
    }
}

@@ -646,16 +646,101 @@
 chScope;
+%0Aimport org.jetbrains.annotations.NotNull;%0Aimport org.jetbrains.annotations.Nullable;
 %0A%0Aimport
@@ -1115,24 +1115,391 @@
 oduleName;%0A%0A
+    private class MatchResult%0A    %7B%0A        private int index;%0A        private String path;%0A%0A        private MatchResult(int index, String path) %7B%0A            this.index = index;%0A            this.path = path;%0A        %7D%0A%0A        private int getIndex() %7B%0A            return index;%0A        %7D%0A%0A        private String getPath() %7B%0A            return path;%0A        %7D%0A    %7D%0A%0A
     public M
@@ -1849,12 +1849,29 @@
 ted 
-void
+@Nullable MatchResult
  get
@@ -1876,16 +1876,25 @@
 etMatch(
+@NotNull 
 String n
@@ -1906,16 +1906,25 @@
 uleName,
+ @NotNull
  DefineS
@@ -1942,16 +1942,25 @@
 atement,
+ @NotNull
  PsiFile
@@ -2097,16 +2097,21 @@
   return
+ null
 ;%0A      
@@ -2425,16 +2425,52 @@
 x = -1;%0A
+        String matchedString = %22%22;%0A%0A
         
@@ -3150,16 +3150,62 @@
 ex = i;%0A
+                matchedString = argumentText;%0A
         
@@ -3252,23 +3252,336 @@
         
-int i=0
+return new MatchResult(matchIndex, matchedString);%0A    %7D%0A%0A    public void updateModuleReference(PsiFile targetFile, MatchResult match, DefineStatement statement)%0A    %7B%0A        statement.getArguments().getExpressions()%5Bmatch.getIndex()%5D.delete();%0A        statement.getFunction().getParameters()%5Bmatch.getIndex()%5D.delete()
 ;%0A    %7D%0A
@@ -3592,16 +3592,26 @@
  public 
+@Nullable 
 PsiFile%5B
@@ -3641,16 +3641,25 @@
 eModule(
+@NotNull 
 VirtualF
@@ -4652,16 +4652,36 @@
         
+ MatchResult match =
  getMatc
@@ -4770,32 +4770,163 @@
 ement, psiFile);
+%0A%0A            if(match != null)%0A            %7B%0A                updateModuleReference(psiFile, match, defineStatement);%0A            %7D
 %0A        %7D%0A     

add match result