package com.chrisfolger.needsmoredojo.intellij.reference;

import com.chrisfolger.needsmoredojo.core.amd.filesystem.DojoModuleFileResolver;
import com.chrisfolger.needsmoredojo.core.amd.objectmodel.DeclareResolver;
import com.chrisfolger.needsmoredojo.core.amd.objectmodel.DeclareStatementItems;
import com.chrisfolger.needsmoredojo.core.amd.psi.AMDPsiUtil;
import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler;
import com.intellij.lang.Language;
import com.intellij.lang.javascript.psi.JSExpression;
import com.intellij.lang.javascript.psi.JSProperty;
import com.intellij.lang.javascript.psi.JSReferenceExpression;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;

public class ThisInheritedGotoDeclarationHandler implements GotoDeclarationHandler
{
    @Nullable
    @Override
    public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int i, Editor editor)
    {
        if(psiElement == null || !psiElement.getLanguage().equals(Language.findLanguageByID("JavaScript")))
        {
            return new PsiElement[0];
        }

        if(!(psiElement.getParent() instanceof JSReferenceExpression))
        {
            return new PsiElement[0];
        }

        if(!psiElement.getText().equals("inherited"))
        {
            return new PsiElement[0];
        }

        // get the modules that this module inherits
        DeclareResolver declareResolver = new DeclareResolver();
        DeclareStatementItems declareObject = declareResolver.getDeclareObject(psiElement.getContainingFile());

        // get the enclosing function of the psiElement
        PsiElement parent = psiElement.getParent();
        JSProperty owningFunction = null;

        while(parent != null)
        {
            if(parent instanceof JSProperty)
            {
                owningFunction = (JSProperty) parent;
            }

            parent = parent.getParent();
        }

        // FIXME: go all the way up the dependency graph!
        DojoModuleFileResolver resolver = new DojoModuleFileResolver();
        // search each inherited module starting from the last one for an equivalent property that matches.
        for (int x = declareObject.getExpressionsToMixin().length - 1; x >= 0; x--)
        {
            JSExpression expression = declareObject.getExpressionsToMixin()[x];

            PsiElement resolvedDefine = AMDPsiUtil.resolveReferencedDefine(expression);
            if(resolvedDefine == null) continue;

            PsiFile resolvedFile = resolver.resolveReferencedFile(psiElement.getProject(), resolvedDefine);
            if(resolvedFile == null) continue;

            JSProperty method = fileHasMethod(resolvedFile, owningFunction.getName());
            if(method != null)
            {
                return new PsiElement[] { method };
            }
            else
            {
                // attempt to recursively search the dependency graph
            }
        }

        return new PsiElement[0];
    }

    private @Nullable JSProperty fileHasMethod(PsiFile file, String methodName)
    {
        // FIXME null checks
        DeclareStatementItems declareObject = new DeclareResolver().getDeclareObject(file);

        for(JSProperty property : declareObject.getMethodsToConvert())
        {
            if(property.getName().equals(methodName))
            {
                return property;
            }
        }

        return null;
    }

    @Nullable
    @Override
    public String getActionText(DataContext dataContext)
    {
        return "Goto this.inherited module";
    }
}

@@ -727,16 +727,61 @@
 Editor;%0A
+import com.intellij.openapi.project.Project;%0A
 import c
@@ -1682,120 +1682,8 @@
 r();
-%0A        DeclareStatementItems declareObject = declareResolver.getDeclareObject(psiElement.getContainingFile());
 %0A%0A  
@@ -2043,32 +2043,523 @@
 t();%0A        %7D%0A%0A
+        PsiElement resolvedMethod = resolveInheritedMethod(psiElement.getContainingFile(), psiElement.getProject(), owningFunction.getName());%0A        if(resolvedMethod != null)%0A        %7B%0A            return new PsiElement%5B%5D %7B resolvedMethod %7D;%0A        %7D%0A%0A        return new PsiElement%5B0%5D;%0A    %7D%0A%0A    private PsiElement resolveInheritedMethod(PsiFile file, Project project, String methodName)%0A    %7B%0A        DeclareStatementItems declareObject = new DeclareResolver().getDeclareObject(file);%0A%0A
         // FIXME
@@ -3160,38 +3160,22 @@
 edFile(p
-siElement.getP
 roject
-()
 , resolv
@@ -3297,32 +3297,18 @@
 le, 
-owningFunction.get
+method
 Name
-()
 );%0A 
@@ -3377,36 +3377,15 @@
 turn
- new PsiElement%5B%5D %7B
  method
- %7D
 ;%0A  
@@ -3538,32 +3538,19 @@
 return n
-ew PsiElement%5B0%5D
+ull
 ;%0A    %7D%0A

make resolution logic reusable