import { tokenize, getLanguageGrammar } from '../../../../polyfill/prism';
import { CodeHighlightParser } from './code-highlight-parser.abstract';
/**
 * Use the local syntax highlight tokenizer to highlight the code content.
 */
export class PrismCodeHighlightParseController extends CodeHighlightParser {
  /**
   * Return true when the content and language are defined.
   *
   * @returns {boolean} - Boolean value.
   */
  canParse() {
    const { code, language } = this.value || { code: '' };
    return code && language && this.isHighlightLanguage(language);
  }
  /**
   * Use tokenizer to parse the string code into an array of nodes with different keywords.
   *
   * @returns {CodeNodeType[]} - The object contains content and highlight type.
   */
  parse() {
    const { code, language } = this.value;
    let result = [];
    const parseLanguage = getLanguageGrammar(language);
    const tokens = tokenize(code, parseLanguage);
    result = this.getHighlightNodes(tokens);
    return this.markLineNumberNodes(result);
  }
  /**
   * Parse the plugin parse result to a usable array.
   *
   * @param {(string | CodeHighlightToken)[]} tokens - The plugin parse result.
   * @param {string} [type] - The keywords. Such as variable, function-name, attr-name...
   * @returns {CodeNodeType[]} - The code node array.
   */
  getHighlightNodes(tokens, type) {
    const nodes = [];
    for (const token of tokens) {
      if (typeof token === 'string') {
        const partials = token.split(/(\n|\t)/);
        const partialsLength = partials.length;
        for (let i = 0; i < partialsLength; i++) {
          const part = partials[i];
          if (part) {
            const textArray = this.splitTag(part);
            nodes.push(...textArray.map((text) => this.createCodeNode(text, type)));
          }
        }
      }
      else {
        const { content } = token;
        if (typeof content === 'string') {
          nodes.push(...this.getHighlightNodes([content], token.type));
        }
        else if (Array.isArray(content)) {
          nodes.push(...this.getHighlightNodes(content, token.type));
        }
      }
    }
    return nodes;
  }
  /**
   * Create a CodeNodeType type object by the code content and highlight type.
   *
   * @param {string} text - The code content.
   * @param {string} highlightType - The highlight type.
   * @returns {CodeNodeType} - The code node object.
   */
  createCodeNode(text, highlightType) {
    return {
      text,
      key: '',
      className: `token ${highlightType || ''}`,
      mark: '',
    };
  }
}
