/**
 * @param {string} value
 * @returns {RegExp}
 * */

/**
 * @param {RegExp | string } re
 * @returns {string}
 */
function source(re) {
  if (!re) return null;
  if (typeof re === "string") return re;

  return re.source;
}

/**
 * @param {RegExp | string } re
 * @returns {string}
 */
function lookahead(re) {
  return concat('(?=', re, ')');
}

/**
 * @param {...(RegExp | string) } args
 * @returns {string}
 */
function concat(...args) {
  const joined = args.map((x) => source(x)).join("");
  return joined;
}

/*
 Language: Groovy
 Author: Guillaume Laforge <glaforge@gmail.com>
 Description: Groovy programming language implementation inspired from Vsevolod's Java mode
 Website: https://groovy-lang.org
 */

function variants(variants, obj = {}) {
  obj.variants = variants;
  return obj;
}

function groovy(hljs) {
  const IDENT_RE = '[A-Za-z0-9_$]+';
  const COMMENT = variants([
    hljs.C_LINE_COMMENT_MODE,
    hljs.C_BLOCK_COMMENT_MODE,
    hljs.COMMENT(
      '/\\*\\*',
      '\\*/',
      {
        relevance : 0,
        contains : [
          {
            // eat up @'s in emails to prevent them to be recognized as doctags
            begin: /\w+@/, relevance: 0
          }, {
            className : 'doctag',
            begin : '@[A-Za-z]+'
          }
        ]
      }
    )
  ]);
  const REGEXP = {
    className: 'regexp',
    begin: /~?\/[^\/\n]+\//,
    contains: [
      hljs.BACKSLASH_ESCAPE
    ]
  };
  const NUMBER = variants([
    hljs.BINARY_NUMBER_MODE,
    hljs.C_NUMBER_MODE,
  ]);
  const STRING = variants([
    {
      begin: /"""/,
      end: /"""/
    }, {
      begin: /'''/,
      end: /'''/
    }, {
      begin: "\\$/",
      end: "/\\$",
      relevance: 10
    },
    hljs.APOS_STRING_MODE,
    hljs.QUOTE_STRING_MODE,
    ],
    { className: "string" }
  );

    return {
        name: 'Groovy',
        keywords: {
            built_in: 'this super',
            literal: 'true false null',
            keyword:
            'byte short char int long boolean float double void ' +
            // groovy specific keywords
            'def as in assert trait ' +
            // common keywords with Java
            'abstract static volatile transient public private protected synchronized final ' +
            'class interface enum if else for while switch case break default continue ' +
            'throw throws try catch finally implements extends new import package return instanceof'
        },
        contains: [
            hljs.SHEBANG(),
            COMMENT,
            STRING,
            REGEXP,
            NUMBER,
            {
                className: 'class',
                beginKeywords: 'class interface trait enum', end: '{',
                illegal: ':',
                contains: [
                    {beginKeywords: 'extends implements'},
                    hljs.UNDERSCORE_TITLE_MODE
                ]
            },
            {
                className: 'meta', begin: '@[A-Za-z]+'
            },
            {
              // highlight map keys and named parameters as attrs
              className: 'attr', begin: IDENT_RE + '[ \t]*:'
            },
            {
              // catch middle element of the ternary operator
              // to avoid highlight it as a label, named parameter, or map key
              begin: /\?/,
              end: /:/,
              contains: [
                COMMENT,
                STRING,
                REGEXP,
                NUMBER,
                'self'
              ]
            },
            {
                // highlight labeled statements
                className: 'symbol',
                begin: '^[ \t]*' + lookahead(IDENT_RE + ':'),
                excludeBegin: true,
                end: IDENT_RE + ':',
                relevance: 0
            }
        ],
        illegal: /#|<\//
    };
}

module.exports = groovy;
