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) return;

        if (cl.isAnnotationType()) {
            holder.registerProblem(parameter, "Class may not refer to an annotation type.");

        } else if (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 root) {
        PsiExpression exp = root;
        while (true) {
            if (exp instanceof PsiClassObjectAccessExpression) {
                PsiType type = ((PsiClassObjectAccessExpression) exp).getOperand().getType();
                if (type instanceof PsiClassType)
                    return ((PsiClassType) type).resolve();

            } else if (exp instanceof PsiReferenceExpression) {
                PsiElement resolved = ((PsiReference) exp).resolve();
                if (resolved instanceof PsiVariable) {
                    PsiExpression initializer = ((PsiVariable) resolved).getInitializer();
                    if (initializer != null)
                        exp = initializer;
                }

            } else {
                return null;
            }
        }
    }
}

@@ -1721,16 +1721,80 @@
 eturn;%0A%0A
+        // TODO: allow instances of known and registered types%0A%0A
         
@@ -1879,32 +1879,26 @@
 ass may not 
-refer to
+be
  an annotati
@@ -1965,162 +1965,514 @@
     
-// TODO: allow interfaces of known (and registered) types%0A            holder.registerProblem(parameter, %22Class may not refer to an interface.%22);%0A        %7D
+holder.registerProblem(parameter, %22Class may not be an interface.%22);%0A%0A        %7D else if (isNonStaticInnerClass(cl)) %7B%0A            holder.registerProblem(parameter, %22Class may not be a non-static inner class.%22);%0A        %7D%0A    %7D%0A%0A    private static boolean isNonStaticInnerClass(@NotNull PsiClass cl) %7B%0A        if (cl.getContainingClass() == null) return false;%0A%0A        PsiModifierList modifiers = cl.getModifierList();%0A        return modifiers == null %7C%7C !modifiers.hasModifierProperty(PsiModifier.STATIC);
 %0A   

Check for non-static inner classes.