package org.basex.gui.text;

import static org.basex.gui.GUIConstants.*;

import java.awt.*;
import java.util.*;

import org.basex.data.*;
import org.basex.util.*;

/**
 * This class defines syntax highlighting for Javascript files.
 *
 * @author BaseX Team 2005-23, BSD License
 * @author Christian Gruen
 */
final class SyntaxJS extends Syntax {
  /** Keywords. */
  private static final HashSet<String> KEYWORDS = new HashSet<>();

  /** Comment. */
  private int comment1;
  /** Comment. */
  private int comment2;
  /** Quote. */
  private int quote;
  /** Variable flag. */
  private boolean var;
  /** Backslash. */
  private boolean back;

  // initialize xquery keys
  static {
    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords
    Collections.addAll(KEYWORDS,
      "await", "break", "case", "catch", "class", "const", "continue", "debugger", "default",
      "delete", "do", "else", "enum", "export", "extends", "finally", "for", "function", "if",
      "implements", "import", "in", "instanceof", "interface", "let", "new", "package", "private",
      "protected", "public", "return", "static", "super", "switch", "this", "throw", "try",
      "typeof", "var", "void", "while", "with", "yield"
    );
  }

  @Override
  public void init(final Color color) {
    super.init(color);
    quote = 0;
    var = false;
    back = false;
    comment1 = 0;
    comment2 = 0;
  }

  @Override
  public Color getColor(final TextIterator iter) {
    final int ch = iter.curr();

    // opened quote
    if(quote != 0) {
      if(back) {
        back = false;
      } else if(ch == quote) {
        quote = 0;
      } else {
        back = ch == '\\';
      }
      return DGRAY;
    }

    // comment
    if(comment1 == 0 && ch == '/') {
      comment1++;
    } else if(comment1 == 1) {
      comment1 = ch == '*' ? 2 : 0;
    } else if(comment1 == 2 && ch == '*') {
      comment1++;
    } else if(comment1 == 3) {
      comment1 = ch == '/' ? 0 : 2;
    }
    // comment
    if(comment2 == 0 && ch == '/') {
      comment2++;
    } else if(comment2 == 1) {
      comment2 = ch == '/' ? 2 : 0;
    } else if(comment2 == 2 && ch == '\n') {
      comment2 = 0;
    }
    if(comment1 != 0 || comment2 != 0) {
      var = false;
      return GRAY;
    }

    // quotes
    if(back) {
      back = false;
    } else if(ch == '\\') {
      back = true;
    } else if(ch == '"' || ch == '\'') {
      quote = ch;
      return DGRAY;
    }

    // variables
    if(ch == '$') {
      var = true;
      return GREEN;
    }
    if(var) {
      var = XMLToken.isNCStartChar(ch);
      return GREEN;
    }

    // digits
    if(Token.digit(ch)) return PURPLE;
    // special characters
    if(!XMLToken.isNCStartChar(ch)) return GRAY;
    // check for keywords
    if(KEYWORDS.contains(iter.currString())) return BLUE;

    // standard text
    return plain;
  }

  @Override
  public byte[] commentOpen() {
    return DataText.JSCOMM_O;
  }

  @Override
  public byte[] commentEnd() {
    return DataText.JSCOMM_C;
  }
}
