/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the "Elastic License
 * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
 * Public License v 1"; you may not use this file except in compliance with, at
 * your election, the "Elastic License 2.0", the "GNU Affero General Public
 * License v3.0 only", or the "Server Side Public License, v 1".
 */

import org.elasticsearch.gradle.testclusters.DefaultTestClustersTask;

apply plugin: 'elasticsearch.validate-rest-spec'
apply plugin: 'elasticsearch.internal-yaml-rest-test'
apply plugin: 'elasticsearch.yaml-rest-compat-test'
apply plugin: 'elasticsearch.internal-cluster-test'

esplugin {
    description = 'An easy, safe and fast scripting language for Elasticsearch'
    classname ='org.elasticsearch.painless.PainlessPlugin'
}

testClusters.configureEach {
    module ':modules:mapper-extras'
    systemProperty 'es.scripting.update.ctx_in_params', 'false'
    // TODO: remove this once cname is prepended to transport.publish_address by default in 8.0
    systemProperty 'es.transport.cname_in_publish_address', 'true'
}

configurations {
    spi
    compileOnlyApi.extendsFrom(spi)
    if (isEclipse) {
        // Eclipse buildship doesn't know about compileOnlyApi
        api.extendsFrom(spi)
    }
}

dependencies {
    api "org.antlr:antlr4-runtime:${versions.antlr4}"
    api 'org.ow2.asm:asm-util:9.8'
    api 'org.ow2.asm:asm-tree:9.8'
    api 'org.ow2.asm:asm-commons:9.8'
    api 'org.ow2.asm:asm-analysis:9.8'
    api 'org.ow2.asm:asm:9.8'
    spi project('spi')
    clusterModules project(':modules:mapper-extras')
}

tasks.named("dependencyLicenses").configure {
    mapping from: /asm-.*/, to: 'asm'
}

tasks.named("yamlRestCompatTestTransform").configure({ task ->
  task.skipTest("painless/146_dense_vector_bit_basic/Dot Product is not supported", "inner product is now supported")
})

restResources {
    restApi {
        include '_common', 'cluster', 'nodes', 'indices', 'index', 'search', 'get', 'bulk', 'update',
                'scripts_painless_execute', 'put_script', 'delete_script', 'capabilities'
    }
}

tasks.named("test").configure {
    // in WhenThingsGoWrongTests we intentionally generate an out of memory error, this prevents the heap from being dumped to disk
    jvmArgs '-XX:-OmitStackTraceInFastThrow', '-XX:-HeapDumpOnOutOfMemoryError'
}

esplugin.bundleSpec.into("spi") {
    from(configurations.spi)
}

/**********************************************
 *           Context API Generation           *
 **********************************************/

sourceSets {
    doc
}

dependencies {
    docImplementation project(':server')
    docImplementation project(':modules:lang-painless')
    docImplementation 'com.github.javaparser:javaparser-core:3.18.0'
    docImplementation 'org.jsoup:jsoup:1.13.1'
    if (isEclipse) {
        /*
         * Eclipse isn't quite "with it" enough to understand the different
         * source sets. This adds the dependency to all source sets so it
         * can compile the doc java files.
         */
        implementation 'com.github.javaparser:javaparser-core:3.18.0'
        implementation 'org.jsoup:jsoup:1.13.1'
    }
}

def generateContextCluster = testClusters.register("generateContextCluster") {
    testDistribution = 'DEFAULT'
}

tasks.register("generateContextDoc", DefaultTestClustersTask) {
    dependsOn sourceSets.doc.runtimeClasspath
    useCluster generateContextCluster
    doFirst {
        project.javaexec {
            mainClass = 'org.elasticsearch.painless.ContextDocGenerator'
            classpath = sourceSets.doc.runtimeClasspath
            systemProperty "cluster.uri", "${-> generateContextCluster.get().singleNode().getAllHttpSocketURI().get(0)}"
        }.assertNormalExitValue()
    }
}
/**********************************************
 *           Context JSON Generation          *
 **********************************************/
def generateContextApiSpecCluster = testClusters.register("generateContextApiSpecCluster") {
    testDistribution = 'DEFAULT'
}

tasks.register("generateContextApiSpec", DefaultTestClustersTask) {
    dependsOn sourceSets.doc.runtimeClasspath
    useCluster generateContextApiSpecCluster
    doFirst {
        project.javaexec {
            mainClass = 'org.elasticsearch.painless.ContextApiSpecGenerator'
            classpath = sourceSets.doc.runtimeClasspath
            systemProperty "cluster.uri", "${-> generateContextApiSpecCluster.get().singleNode().getAllHttpSocketURI().get(0)}"
            systemProperty "jdksrc", providers.systemProperty("jdksrc").getOrNull()
            systemProperty "packageSources", providers.systemProperty("packageSources").getOrNull()
        }.assertNormalExitValue()
    }
}

/**********************************************
 *            Parser regeneration             *
 **********************************************/

configurations {
    regenerate
}

dependencies {
  regenerate "org.antlr:antlr4:${versions.antlr4}"
}

String grammarPath = 'src/main/antlr'
// TODO: we shouldn't be generating files into the main source directory. Then we wouldn't
// need to customize the spotless config below.
String outputPath = 'src/main/java/org/elasticsearch/painless/antlr'

pluginManager.withPlugin('com.diffplug.spotless') {
    spotless {
        java {
            targetExclude "${outputPath}/*.java"
        }
    }
}

tasks.register("cleanGenerated", Delete) {
    delete fileTree(grammarPath) {
        include '*Painless*.tokens'
    }
    delete fileTree(outputPath) {
        include 'Painless*.java'
    }
}

tasks.register("regenLexer", JavaExec) {
    dependsOn "cleanGenerated"
    mainClass = 'org.antlr.v4.Tool'
    classpath = configurations.regenerate
    systemProperty 'file.encoding', 'UTF-8'
    systemProperty 'user.language', 'en'
    systemProperty 'user.country', 'US'
    systemProperty 'user.variant', ''
    args '-Werror',
            '-package', 'org.elasticsearch.painless.antlr',
            '-o', outputPath,
            "${file(grammarPath)}/PainlessLexer.g4"
}

tasks.register("regenParser", JavaExec) {
    dependsOn "regenLexer"
    mainClass = 'org.antlr.v4.Tool'
    classpath = configurations.regenerate
    systemProperty 'file.encoding', 'UTF-8'
    systemProperty 'user.language', 'en'
    systemProperty 'user.country', 'US'
    systemProperty 'user.variant', ''
    args '-Werror',
            '-package', 'org.elasticsearch.painless.antlr',
            '-no-listener',
            '-visitor',
            // '-Xlog',
            '-o', outputPath,
            "${file(grammarPath)}/PainlessParser.g4"
}

tasks.register("regen") {
    dependsOn "regenParser"
    doLast {
        // moves token files to grammar directory for use with IDE's
        ant.move(file: "${outputPath}/PainlessLexer.tokens", toDir: grammarPath)
        ant.move(file: "${outputPath}/PainlessParser.tokens", toDir: grammarPath)
        // make the generated classes package private
        ant.replaceregexp(match: 'public ((interface|class) \\QPainless\\E\\w+)',
                replace: '\\1',
                encoding: 'UTF-8') {
            fileset(dir: outputPath, includes: 'Painless*.java')
        }
        // make the lexer abstract
        ant.replaceregexp(match: '(class \\QPainless\\ELexer)',
                replace: 'abstract \\1',
                encoding: 'UTF-8') {
            fileset(dir: outputPath, includes: 'PainlessLexer.java')
        }
        // nuke timestamps/filenames in generated files
        ant.replaceregexp(match: '\\Q// Generated from \\E.*',
                replace: '\\/\\/ ANTLR GENERATED CODE: DO NOT EDIT',
                encoding: 'UTF-8') {
            fileset(dir: outputPath, includes: 'Painless*.java')
        }
        // remove tabs in antlr generated files
        ant.replaceregexp(match: '\t', flags: 'g', replace: '  ', encoding: 'UTF-8') {
            fileset(dir: outputPath, includes: 'Painless*.java')
        }
        // fix line endings
        ant.fixcrlf(srcdir: outputPath, eol: 'lf') {
            patternset(includes: 'Painless*.java')
        }
    }
}

/**********************************************
 *         Suggest lexer regeneration          *
 **********************************************/

configurations {
    suggestRegenerate
}

dependencies {
  regenerate "org.antlr:antlr4:${versions.antlr4}"
}

String suggestGrammarPath = 'src/main/antlr'
String suggestOutputPath = 'src/main/java/org/elasticsearch/painless/antlr'

tasks.register("cleanSuggestGenerated", Delete) {
    delete fileTree(suggestGrammarPath) {
        include 'SuggestLexer.tokens'
    }
    delete fileTree(suggestOutputPath) {
        include 'Suggest*.java'
    }
}

tasks.register("regenSuggestLexer", JavaExec) {
    dependsOn "cleanSuggestGenerated"
    mainClass = 'org.antlr.v4.Tool'
    classpath = configurations.regenerate
    systemProperty 'file.encoding', 'UTF-8'
    systemProperty 'user.language', 'en'
    systemProperty 'user.country', 'US'
    systemProperty 'user.variant', ''
    args '-Werror',
            '-package', 'org.elasticsearch.painless.antlr',
            '-o', suggestOutputPath,
            "${file(suggestGrammarPath)}/SuggestLexer.g4"
}

tasks.register("regenSuggest") {
    dependsOn "regenSuggestLexer"
    doLast {
        // moves token files to grammar directory for use with IDE's
        ant.move(file: "${suggestOutputPath}/SuggestLexer.tokens", toDir: suggestGrammarPath)
        // make the lexer abstract
        ant.replaceregexp(match: '(class \\QSuggest\\ELexer)',
                replace: 'abstract \\1',
                encoding: 'UTF-8') {
            fileset(dir: suggestOutputPath, includes: 'SuggestLexer.java')
        }
        // nuke timestamps/filenames in generated files
        ant.replaceregexp(match: '\\Q// Generated from \\E.*',
                replace: '\\/\\/ ANTLR GENERATED CODE: DO NOT EDIT',
                encoding: 'UTF-8') {
            fileset(dir: suggestOutputPath, includes: 'Suggest*.java')
        }
        // remove tabs in antlr generated files
        ant.replaceregexp(match: '\t', flags: 'g', replace: '  ', encoding: 'UTF-8') {
            fileset(dir: suggestOutputPath, includes: 'Suggest*.java')
        }
        // fix line endings
        ant.fixcrlf(srcdir: suggestOutputPath, eol: 'lf') {
            patternset(includes: 'Suggest*.java')
        }
    }
}
