package com.intellij.tasks.jira.jql.codeinsight;

import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.Annotator;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.tasks.jira.jql.psi.*;
import org.jetbrains.annotations.NotNull;

/**
 * Checks the following errors:
 * <ol>
 * <li>Not list operand was given inside 'IN' clauses or vice-versa
 * <li>constants 'empty' or 'null' weren't used inside 'IS' clauses or vice-versa
 * </ol>
 * @author Mikhail Golubev
 */
public class JqlAnnotator implements Annotator {
  @Override
  public void annotate(@NotNull PsiElement element, final @NotNull AnnotationHolder holder) {
    element.accept(new JqlElementVisitor() {
      @Override
      public void visitEmptyValue(JqlEmptyValue emptyValue) {
        JqlSimpleClause clause = PsiTreeUtil.getParentOfType(emptyValue, JqlSimpleClause.class);
        if (clause != null && !isEmptyClause(clause)) {
          holder.createErrorAnnotation(emptyValue, String.format("Not expecting '%s' here", emptyValue.getText()));
        }
      }

      @Override
      public void visitJqlList(JqlList list) {
        JqlSimpleClause clause = PsiTreeUtil.getParentOfType(list, JqlSimpleClause.class);
        if (clause != null && !isListClause(clause)) {
          holder.createErrorAnnotation(list, String.format("Not expecting list of values here"));
        }
      }

      @Override
      public void visitJqlSimpleClause(JqlSimpleClause clause) {
        JqlOperand operand = clause.getOperand();
        if (operand == null) {
          return;
        }
        boolean operandIsListLiteral = operand instanceof JqlList;
        boolean operandIsListFunction = false;
        if (operand instanceof JqlFunctionCall) {
          JqlFunctionCall functionCall = (JqlFunctionCall)operand;
          JqlStandardFunction standardFunction = JqlStandardFunction.byName(functionCall.getFunctionName().getText());
          operandIsListFunction = standardFunction != null && standardFunction.hasMultipleResults();
        }
        boolean hasListOperand = operandIsListLiteral || operandIsListFunction;
        if (isListClause(clause) && !hasListOperand) {
          holder.createErrorAnnotation(operand, "Expecting list of values here");
        }

        boolean hasEmptyOperand = operand instanceof JqlEmptyValue;
        if (isEmptyClause(clause) && !hasEmptyOperand) {
          holder.createErrorAnnotation(operand, "Expecting 'empty' or 'null' here");
        }
      }
    });
  }

  private static boolean isEmptyClause(JqlTerminalClause clause) {
    JqlTerminalClause.Type clauseType = clause.getType();
    return clauseType == JqlTerminalClause.Type.IS || clauseType == JqlTerminalClause.Type.IS_NOT;
  }

  private static boolean isListClause(JqlTerminalClause clause) {
    JqlTerminalClause.Type clauseType = clause.getType();
    return clauseType != null && clauseType.isListOperator();
  }
}

@@ -43,16 +43,64 @@
 sight;%0A%0A
+import com.intellij.lang.annotation.Annotation;%0A
 import c
@@ -359,16 +359,115 @@
 l;%0A%0A
-/**%0A * C
+import static com.intellij.openapi.editor.DefaultLanguageHighlighterColors.CONSTANT;%0A%0A/**%0A * At first, it c
 heck
@@ -505,20 +505,20 @@
 %0A * %3Cli%3E
-Not 
+not-
 list ope
@@ -544,34 +544,35 @@
 de 'IN' clauses 
-or
+and
  vice-versa%0A * %3C
@@ -635,18 +635,19 @@
 clauses 
-or
+and
  vice-ve
@@ -659,16 +659,129 @@
 * %3C/ol%3E%0A
+ *%0A *%0A * It also highlights JQL identifiers (fields and function names) differently%0A * from unquoted strings.%0A *%0A
  * @auth
@@ -954,24 +954,44 @@
 r holder) %7B%0A
+    // error checks%0A
     element.
@@ -2817,24 +2817,272 @@
    %7D%0A      %7D
+%0A%0A      @Override%0A      public void visitJqlIdentifier(JqlIdentifier identifier) %7B%0A        Annotation annotation = holder.createInfoAnnotation(identifier, null);%0A        annotation.setEnforcedTextAttributes(CONSTANT.getDefaultAttributes());%0A      %7D
 %0A    %7D);%0A  %7D

Update JqlAnnotator to highlight JqlIdentifiers as constants, not as strings, to be consistent with YouTrack queries colors