// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.python.inspections;

import com.intellij.codeInspection.LocalInspectionToolSession;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElementVisitor;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.codeInsight.override.PyOverrideImplementUtil;
import com.jetbrains.python.codeInsight.typing.PyProtocolsKt;
import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider;
import com.jetbrains.python.inspections.quickfix.PyImplementMethodsQuickFix;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.types.PyClassLikeType;
import com.jetbrains.python.psi.types.PyType;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;

import static com.jetbrains.python.psi.PyUtil.as;

public class PyAbstractClassInspection extends PyInspection {
  @Nls
  @NotNull
  @Override
  public String getDisplayName() {
    return PyBundle.message("INSP.NAME.abstract.class");
  }

  @NotNull
  @Override
  public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder,
                                        boolean isOnTheFly,
                                        @NotNull LocalInspectionToolSession session) {
    return new Visitor(holder, session);
  }

  private static class Visitor extends PyInspectionVisitor {
    public Visitor(@Nullable ProblemsHolder holder, @NotNull LocalInspectionToolSession session) {
      super(holder, session);
    }

    @Override
    public void visitPyClass(PyClass pyClass) {
      if (isAbstract(pyClass) || PyProtocolsKt.isProtocol(pyClass, myTypeEvalContext)) {
        return;
      }
      final List<PyFunction> toImplement = PyOverrideImplementUtil.getAllSuperAbstractMethods(pyClass, myTypeEvalContext);
      final ASTNode nameNode = pyClass.getNameNode();
      if (!toImplement.isEmpty() && nameNode != null) {
        registerProblem(nameNode.getPsi(),
                        PyBundle.message("INSP.NAME.abstract.class.$0.must.implement", pyClass.getName()),
                        new PyImplementMethodsQuickFix(pyClass, toImplement));
      }
    }

    private boolean isAbstract(@NotNull PyClass pyClass) {
      final PyType metaClass = pyClass.getMetaClassType(myTypeEvalContext);
      if (metaClass instanceof PyClassLikeType && PyNames.ABC_META_CLASS.equals(metaClass.getName())) {
        return true;
      }
      if (metaClass == null) {
        final PyExpression metaClassExpr = as(pyClass.getMetaClassExpression(), PyReferenceExpression.class);
        if (metaClassExpr != null && PyNames.ABC_META_CLASS.equals(metaClassExpr.getName())) {
          return true;
        }
      }
      for (PyFunction method : pyClass.getMethods()) {
        if (PyKnownDecoratorUtil.hasAbstractDecorator(method, myTypeEvalContext)) {
          return true;
        }
      }
      return false;
    }
  }
}

@@ -611,124 +611,140 @@
 hon.
-codeInsight.typing.PyTypingTypeProvider;%0Aimport com.jetbrains.python.inspections.quickfix.PyImplementMethodsQuickFix
+inspections.quickfix.PyImplementMethodsQuickFix;%0Aimport com.jetbrains.python.psi.PyClass;%0Aimport com.jetbrains.python.psi.PyFunction
 ;%0Aim
@@ -777,9 +777,28 @@
 psi.
-*
+PyKnownDecoratorUtil
 ;%0Aim
@@ -854,54 +854,8 @@
 pe;%0A
-import com.jetbrains.python.psi.types.PyType;%0A
 impo
@@ -934,124 +934,30 @@
 ll;%0A
-import org.jetbrains.annotations.Nullable;%0A%0Aimport java.util.List;%0A%0Aimport static com.jetbrains.python.psi.PyUtil.as
+%0Aimport java.util.List
 ;%0A%0Ap
@@ -1493,26 +1493,28 @@
 sitor %7B%0A
+%0A
     p
-ublic
+rivate
  Visitor
@@ -1520,15 +1520,14 @@
 r(@N
+otN
 ull
-able
  Pro
@@ -2355,16 +2355,25 @@
 final Py
+ClassLike
 Type met
@@ -2406,16 +2406,23 @@
 assType(
+false, 
 myTypeEv
@@ -2434,287 +2434,11 @@
 text
-);%0A      if (metaClass instanceof PyClassLikeType && PyNames.ABC_META_CLASS.equals(metaClass.getName())) %7B%0A        return true;%0A      %7D%0A      if (metaClass == null) %7B%0A        final PyExpression metaClassExpr = as(pyClass.getMetaClassExpression(), PyReferenceExpression.class
 );%0A
-  
     
@@ -2452,20 +2452,16 @@
 etaClass
-Expr
  != null
@@ -2507,12 +2507,8 @@
 lass
-Expr
 .get
@@ -2518,34 +2518,32 @@
 e())) %7B%0A        
-  
 return true;%0A   
@@ -2535,34 +2535,24 @@
 eturn true;%0A
-        %7D%0A
       %7D%0A    

Simplify determining if class is abstract (PY-30789)