package fi.evident.dalesbred.plugin.idea;

import com.intellij.codeInspection.BaseJavaLocalInspectionTool;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.patterns.PsiMethodCallPattern;
import com.intellij.psi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import static fi.evident.dalesbred.plugin.idea.DalesbredPatterns.psiDalesbredFindMethodCall;

public class DalesbredUninstantiableResultInspection extends BaseJavaLocalInspectionTool {

    private static final PsiMethodCallPattern FIND_METHOD_CALL = psiDalesbredFindMethodCall();

    @NotNull
    @Override
    public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
        return new JavaElementVisitor() {

            @Override
            public void visitMethodCallExpression(PsiMethodCallExpression expression) {
                if (FIND_METHOD_CALL.accepts(expression)) {
                    String methodName = expression.getMethodExpression().getReferenceName();
                    PsiExpression[] parameters = expression.getArgumentList().getExpressions();

                    if ("findMap".equals(methodName)) {
                        verifyParameterIsInstantiable(parameters[0], holder);
                        verifyParameterIsInstantiable(parameters[1], holder);
                    } else {
                        verifyParameterIsInstantiable(parameters[0], holder);
                    }
                }
            }
        };
    }

    private static void verifyParameterIsInstantiable(@NotNull PsiExpression parameter, @NotNull ProblemsHolder holder) {
        PsiClass cl = resolveType(parameter);
        if (cl != null && cl.isInterface()) {
            // TODO: allow interfaces of known (and registered) types
            holder.registerProblem(parameter, "Class may not refer to an interface.");
        }
    }

    @Nullable
    private static PsiClass resolveType(@NotNull PsiExpression value) {
        if (value instanceof PsiClassObjectAccessExpression) {
            PsiType type = ((PsiClassObjectAccessExpression) value).getOperand().getType();
            if (type instanceof PsiClassType)
                return ((PsiClassType) type).resolve();
        }

        return null;
    }
}

@@ -1985,19 +1985,75 @@
 ression 
-val
+root) %7B%0A        PsiExpression exp = root;%0A        while (tr
 ue) %7B%0A  
@@ -2062,17 +2062,19 @@
     
-if (value
+    if (exp
  ins
@@ -2107,32 +2107,36 @@
 ssExpression) %7B%0A
+    
             PsiT
@@ -2184,13 +2184,11 @@
 on) 
-value
+exp
 ).ge
@@ -2217,24 +2217,28 @@
             
+  
+  
 if (type ins
@@ -2259,16 +2259,20 @@
 ssType)%0A
+    
         
@@ -2319,24 +2319,26 @@
 olve();%0A
+%0A 
         
 %7D%0A%0A     
@@ -2333,31 +2333,459 @@
     
-%7D%0A%0A        return null;
+   %7D else if (exp instanceof PsiReferenceExpression) %7B%0A                PsiElement resolved = ((PsiReference) exp).resolve();%0A                if (resolved instanceof PsiVariable) %7B%0A                    PsiExpression initializer = ((PsiVariable) resolved).getInitializer();%0A                    if (initializer != null)%0A                        exp = initializer;%0A                %7D%0A%0A            %7D else %7B%0A                return null;%0A            %7D%0A        %7D
 %0A   

Resolve type of expression through local variables.