// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.tasks.jira.jql;

import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.tree.IElementType;
import com.intellij.tasks.jira.jql.psi.impl.*;
import org.jetbrains.annotations.NotNull;

/**
 * @author Mikhail Golubev
 *
 * Slightly refactored JQL grammar. See original ANTLR parser grammar at:
 * http://jira.stagingonserver.com/jira-project/jira-components/jira-core/src/main/antlr3/com/atlassian/jira/jql/parser/antlr/Jql.g
 *
 * query ::= or_clause [order_by]
 * or_clause ::= and_clause {or_op and_clause}
 * and_clause ::= not_expr {and_op not_expr}
 * not_expr ::= not_op not_expr
 *            | subclause
 *            | terminal_clause
 * subclause ::= "(" or_clause ")"
 * terminal_clause ::= simple_clause
 *                   | was_clause
 *                   | changed_clause
 * simple_clause ::= field simple_op value
 * # although this is not mentioned in JQL manual, usage of both "from" and "to" predicates in "was" clause is legal
 * was_clause ::= field "was" ["not"] ["in"] operand {history_predicate}
 * changed_clause ::= field "changed" {history_predicate}
 * simple_op ::= "="
 *             | "!="
 *             | "~"
 *             | "!~"
 *             | "<"
 *             | ">"
 *             | "<="
 *             | ">="
 *             | ["not"] "in"
 *             | "is" ["not"]
 * not_op ::= "not" | "!"
 * and_op ::= "and" | "&&" | "&"
 * or_op ::= "or" | "||" | "|"
 * history_predicate ::= "from" operand
 *                     | "to" operand
 *                     | "by" operand
 *                     | "before" operand
 *                     | "after" operand
 *                     | "on" operand
 *                     | "during" operand
 * field ::= string
 *         | NUMBER
 *         | CUSTOM_FIELD
 * operand ::= empty
 *           | string
 *           | NUMBER
 *           | func
 *           | list
 * empty ::= "empty" | "null"
 * list ::= "(" operand {"," operand} ")"
 * func ::= fname "(" arg_list ")"
 * # function name can be even number (!)
 * fname ::= string | NUMBER
 * arg_list ::= argument {"," argument}
 * argument ::= string | NUMBER
 * string ::= SQUOTED_STRING
 *          | QUOTED_STRING
 *          | UNQOUTED_STRING
 * order_by ::= "order" "by" sort_key {sort_key}
 * sort_key ::= field ("asc" | "desc")
 *
 */
public interface JqlElementTypes {
  IElementType QUERY = new JqlElementType("QUERY");
  IElementType OR_CLAUSE = new JqlElementType("OR_CLAUSE");
  IElementType AND_CLAUSE = new JqlElementType("AND_CLAUSE");
  IElementType NOT_CLAUSE = new JqlElementType("NOT_CLAUSE");
  // actually parenthesized clause, named so to be consistent with official grammar
  IElementType SUB_CLAUSE = new JqlElementType("SUB_CLAUSE");
  //IElementType TERMINAL_CLAUSE = new JqlElementType("TERMINAL_CLAUSE");
  // field (= | != | ~ | !~ | < | > | <= | >= | is [not] | [not] in) value
  IElementType SIMPLE_CLAUSE = new JqlElementType("SIMPLE_CLAUSE");
  // field was [not] [in] value {history_predicate}
  IElementType WAS_CLAUSE = new JqlElementType("WAS_CLAUSE");
  // field changed {history_predicate}
  IElementType CHANGED_CLAUSE = new JqlElementType("CHANGED_CLAUSE");
  IElementType LIST = new JqlElementType("LIST");
  IElementType ORDER_BY = new JqlElementType("ORDER_BY");
  IElementType IDENTIFIER = new JqlElementType("IDENTIFIER");
  IElementType LITERAL = new JqlElementType("LITERAL");
  IElementType FUNCTION_CALL = new JqlElementType("FUNCTION_CALL");
  IElementType ARGUMENT_LIST = new JqlElementType("ARGUMENT_LIST");
  IElementType SORT_KEY = new JqlElementType("SORT_KEY");
  IElementType EMPTY = new JqlElementType("EMPTY");
  IElementType HISTORY_PREDICATE = new JqlElementType("HISTORY_PREDICATE");

  final class Factory {
    public static @NotNull PsiElement createElement(@NotNull ASTNode node) {
      final IElementType type = node.getElementType();
      if (type == QUERY) {
        return new JqlQueryImpl(node);
      }
      else if (type == OR_CLAUSE) {
        return new JqlOrClauseImpl(node);
      }
      else if (type == AND_CLAUSE) {
        return new JqlAndClauseImpl(node);
      }
      else if (type == NOT_CLAUSE) {
        return new JqlNotClauseImpl(node);
      }
      else if (type == SUB_CLAUSE) {
        return new JqlSubClauseImpl(node);
      }
      else if (type == SIMPLE_CLAUSE) {
        return new JqlSimpleClauseImpl(node);
      }
      else if (type == WAS_CLAUSE) {
        return new JqlWasClauseImpl(node);
      }
      else if (type == CHANGED_CLAUSE) {
        return new JqlChangedClauseImpl(node);
      }
      else if (type == LIST) {
        return new JqlListImpl(node);
      }
      else if (type == ORDER_BY) {
        return new JqlOrderByImpl(node);
      }
      else if (type == IDENTIFIER) {
        return new JqlIdentifierImpl(node);
      }
      else if (type == LITERAL) {
        return new JqlLiteralImpl(node);
      }
      else if (type == FUNCTION_CALL) {
        return new JqlFunctionCallImpl(node);
      }
      else if (type == ARGUMENT_LIST) {
        return new JqlArgumentListImpl(node);
      }
      else if (type == SORT_KEY) {
        return new JqlSortKeyImpl(node);
      }
      else if (type == EMPTY) {
        return new JqlEmptyValueImpl(node);
      }
      else if (type == HISTORY_PREDICATE) {
        return new JqlHistoryPredicateImpl(node);
      }
      return new ASTWrapperPsiElement(node);
    }
  }
}
