package com.jetbrains.python.inspections;

import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.ProblemHighlightType;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.ResolveResult;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.actions.AddGlobalQuickFix;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
import com.jetbrains.python.codeInsight.dataflow.scope.Scope;
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeVariable;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.PyReferenceExpression;
import com.jetbrains.python.psi.impl.*;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;

/**
 * @author oleg
 */
public class PyUnboundLocalVariableInspection extends LocalInspectionTool {

  @Nls
  @NotNull
  @Override
  public String getGroupDisplayName() {
    return PyBundle.message("INSP.GROUP.python");
  }

  @NotNull
  @Nls
  public String getDisplayName() {
    return PyBundle.message("INSP.NAME.unbound");
  }

  @NotNull
  @NonNls
  public String getShortName() {
    return "PyUnboundLocalVariableInspection";
  }

  public boolean isEnabledByDefault() {
    return false;
  }

  @NotNull
  public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
    return new PyInspectionVisitor(holder){
      @Override
      public void visitPyReferenceExpression(final PyReferenceExpression node) {
        // Ignore callee expressions
        if (PyCallExpressionNavigator.getPyCallExpressionByCallee(node) != null){
          return;
        }
        // Ignore global statements arguments
        if (PyGlobalStatementNavigator.getPyGlobalStatementByArgument(node) != null){
          return;
        }
        // Ignore qualifier inspections
        final PyExpression qualifier = node.getQualifier();
        if (qualifier != null){
          qualifier.accept(this);
          return;
        }
        // Ignore import arguments
        if (PyImportStatementNavigator.getImportStatementByElement(node) != null){
          return;
        }
        final ScopeOwner owner = PsiTreeUtil.getParentOfType(node, ScopeOwner.class);
        if (owner == null){
          return;
        }
        final String name = node.getReferencedName();
        final Scope scope = owner.getScope();
        // Ignore globals and if scope even doesn't contain such a declaration
        if (scope.isGlobal(name) || (!scope.containsDeclaration(name))){
          return;
        }
        final ScopeVariable variable = scope.getDeclaredVariable(node, name);
        if (variable == null) {
          boolean resolves2LocalVariable = false;
          boolean resolve2Scope = true;
          for (ResolveResult result : node.multiResolve(true)) {
            final PsiElement element = result.getElement();
            if (element == null){
              continue;
            }
            // Ingore builtin elements here
            final String fileName = element.getContainingFile().getName();
            if (PyBuiltinCache.BUILTIN_FILE.equals(fileName) || PyBuiltinCache.BUILTIN_FILE_3K.equals(fileName)){
              continue;
            }
            if (PyAssignmentStatementNavigator.getStatementByTarget(element)!=null){
              resolves2LocalVariable = true;
              resolve2Scope = PsiTreeUtil.isAncestor(owner, element, false);
              break;
            }
          }
          // Ignore this if can resolve not to local variable
          if (resolves2LocalVariable) {
            if (owner instanceof PyClass || owner instanceof PyFunction){
              registerProblem(node, PyBundle.message("INSP.unbound.local.variable", node.getName()),
                              ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
                              null,
                              new AddGlobalQuickFix());
            } else {
              if (resolve2Scope){
                registerProblem(node, PyBundle.message("INSP.unbound.name.not.defined", node.getName()));
              }
            }
          }}
      }
    };
  }

  @NotNull
  public HighlightDisplayLevel getDefaultLevel() {
    return HighlightDisplayLevel.WARNING;
  }

}
@@ -745,160 +745,9 @@
 psi.
-PyClass;%0Aimport com.jetbrains.python.psi.PyExpression;%0Aimport com.jetbrains.python.psi.PyFunction;%0Aimport com.jetbrains.python.psi.PyReferenceExpression
+*
 ;%0Aim
@@ -1651,24 +1651,173 @@
 ion node) %7B%0A
+        if (PsiTreeUtil.getParentOfType(node, PyExceptPart.class) != null)%7B%0A          // TODO%5Boleg%5D more accurate check%0A          return;%0A        %7D%0A%0A
         // I

Fix of unbound local variable inside except parts