//version: 1685785062
/*
 DO NOT CHANGE THIS FILE!
 Also, you may replace this file at any time if there is an update available.
 Please check https://github.com/GTNewHorizons/ExampleMod1.7.10/blob/master/build.gradle for updates.
 */


import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import com.gtnewhorizons.retrofuturagradle.ObfuscationAttribute
import com.gtnewhorizons.retrofuturagradle.mcp.ReobfuscatedJar
import com.gtnewhorizons.retrofuturagradle.minecraft.RunMinecraftTask
import com.gtnewhorizons.retrofuturagradle.util.Distribution
import com.matthewprenger.cursegradle.CurseArtifact
import com.matthewprenger.cursegradle.CurseRelation
import com.modrinth.minotaur.dependencies.ModDependency
import com.modrinth.minotaur.dependencies.VersionDependency
import org.gradle.internal.logging.text.StyledTextOutput.Style
import org.gradle.internal.logging.text.StyledTextOutputFactory
import org.gradle.internal.xml.XmlTransformer
import org.jetbrains.gradle.ext.Application
import org.jetbrains.gradle.ext.Gradle

import javax.inject.Inject
import java.nio.file.Files
import java.nio.file.Paths
import java.util.concurrent.TimeUnit

buildscript {
    repositories {
        mavenCentral()

        maven {
            name 'forge'
            url 'https://maven.minecraftforge.net'
        }
        maven {
            // GTNH RetroFuturaGradle and ASM Fork
            name "GTNH Maven"
            url "http://jenkins.usrv.eu:8081/nexus/content/groups/public/"
            allowInsecureProtocol = true
        }
        maven {
            name 'sonatype'
            url 'https://oss.sonatype.org/content/repositories/snapshots/'
        }
        maven {
            name 'Scala CI dependencies'
            url 'https://repo1.maven.org/maven2/'
        }

        mavenLocal()
    }
}
plugins {
    id 'java-library'
    id "org.jetbrains.gradle.plugin.idea-ext" version "1.1.7"
    id 'eclipse'
    id 'scala'
    id 'maven-publish'
    id 'org.jetbrains.kotlin.jvm' version '1.8.0' apply false
    id 'org.jetbrains.kotlin.kapt' version '1.8.0' apply false
    id 'com.google.devtools.ksp' version '1.8.0-1.0.9' apply false
    id 'org.ajoberstar.grgit' version '4.1.1' // 4.1.1 is the last jvm8 supporting version, unused, available for addon.gradle
    id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
    id 'com.palantir.git-version' version '3.0.0' apply false
    id 'de.undercouch.download' version '5.4.0'
    id 'com.github.gmazzo.buildconfig' version '3.1.0' apply false // Unused, available for addon.gradle
    id 'com.diffplug.spotless' version '6.13.0' apply false // 6.13.0 is the last jvm8 supporting version
    id 'com.modrinth.minotaur' version '2.+' apply false
    id 'com.matthewprenger.cursegradle' version '1.4.0' apply false
    id 'com.gtnewhorizons.retrofuturagradle' version '1.3.14'
}

print("You might want to check out './gradlew :faq' if your build fails.\n")

boolean settingsupdated = verifySettingsGradle()
settingsupdated = verifyGitAttributes() || settingsupdated
if (settingsupdated)
    throw new GradleException("Settings has been updated, please re-run task.")

// In submodules, .git is a file pointing to the real git dir
if (project.file('.git/HEAD').isFile() || project.file('.git').isFile()) {
    apply plugin: 'com.palantir.git-version'
}

def out = services.get(StyledTextOutputFactory).create('an-output')

def projectJavaVersion = JavaLanguageVersion.of(8)

boolean disableSpotless = project.hasProperty("disableSpotless") ? project.disableSpotless.toBoolean() : false

checkPropertyExists("modName")
checkPropertyExists("modId")
checkPropertyExists("modGroup")
checkPropertyExists("autoUpdateBuildScript")
checkPropertyExists("minecraftVersion")
checkPropertyExists("forgeVersion")
checkPropertyExists("replaceGradleTokenInFile")
checkPropertyExists("gradleTokenVersion")
checkPropertyExists("apiPackage")
checkPropertyExists("accessTransformersFile")
checkPropertyExists("usesMixins")
checkPropertyExists("mixinPlugin")
checkPropertyExists("mixinsPackage")
checkPropertyExists("coreModClass")
checkPropertyExists("containsMixinsAndOrCoreModOnly")
checkPropertyExists("usesShadowedDependencies")
checkPropertyExists("developmentEnvironmentUserName")

propertyDefaultIfUnset("generateGradleTokenClass", "")
propertyDefaultIfUnset("includeWellKnownRepositories", true)
propertyDefaultIfUnset("noPublishedSources", false)
propertyDefaultIfUnset("usesMixinDebug", project.usesMixins)
propertyDefaultIfUnset("forceEnableMixins", false)
propertyDefaultIfUnset("channel", "stable")
propertyDefaultIfUnset("mappingsVersion", "12")
propertyDefaultIfUnset("modrinthProjectId", "")
propertyDefaultIfUnset("modrinthRelations", "")
propertyDefaultIfUnset("curseForgeProjectId", "")
propertyDefaultIfUnset("curseForgeRelations", "")
propertyDefaultIfUnset("minimizeShadowedDependencies", true)
propertyDefaultIfUnset("relocateShadowedDependencies", true)
// Deprecated properties (kept for backwards compat)
propertyDefaultIfUnset("gradleTokenModId", "")
propertyDefaultIfUnset("gradleTokenModName", "")
propertyDefaultIfUnset("gradleTokenGroupName", "")

propertyDefaultIfUnset("enableModernJavaSyntax", false) // On by default for new projects only
propertyDefaultIfUnset("enableGenericInjection", false) // On by default for new projects only

// this is meant to be set using the user wide property file. by default we do nothing.
propertyDefaultIfUnset("ideaOverrideBuildType", "") // Can be nothing, "gradle" or "idea"

project.extensions.add(com.diffplug.blowdryer.Blowdryer, "Blowdryer", com.diffplug.blowdryer.Blowdryer) // Make blowdryer available in "apply from:" scripts
if (!disableSpotless) {
    apply plugin: 'com.diffplug.spotless'
    apply from: Blowdryer.file('spotless.gradle')
}

String javaSourceDir = "src/main/java/"
String scalaSourceDir = "src/main/scala/"
String kotlinSourceDir = "src/main/kotlin/"

if (usesShadowedDependencies.toBoolean()) {
    apply plugin: "com.github.johnrengelman.shadow"
}

java {
    toolchain {
        if (enableModernJavaSyntax.toBoolean()) {
            languageVersion.set(JavaLanguageVersion.of(17))
        } else {
            languageVersion.set(projectJavaVersion)
        }
        vendor.set(JvmVendorSpec.AZUL)
    }
    if (!noPublishedSources) {
        withSourcesJar()
    }
}

tasks.withType(JavaCompile).configureEach {
    options.encoding = "UTF-8"
}

tasks.withType(ScalaCompile).configureEach {
    options.encoding = "UTF-8"
}

pluginManager.withPlugin('org.jetbrains.kotlin.jvm') {
    // If Kotlin is enabled in the project
    kotlin {
        jvmToolchain(8)
    }
    // Kotlin hacks our source sets, so we hack Kotlin's tasks
    def disabledKotlinTaskList = [
        "kaptGenerateStubsMcLauncherKotlin",
        "kaptGenerateStubsPatchedMcKotlin",
        "kaptGenerateStubsInjectedTagsKotlin",
        "compileMcLauncherKotlin",
        "compilePatchedMcKotlin",
        "compileInjectedTagsKotlin",
        "kaptMcLauncherKotlin",
        "kaptPatchedMcKotlin",
        "kaptInjectedTagsKotlin",
        "kspMcLauncherKotlin",
        "kspPatchedMcKotlin",
        "kspInjectedTagsKotlin",
    ]
    tasks.configureEach { task ->
        if (task.name in disabledKotlinTaskList) {
            task.enabled = false
        }
    }
}

configurations {
    create("runtimeOnlyNonPublishable") {
        description = "Runtime only dependencies that are not published alongside the jar"
        canBeConsumed = false
        canBeResolved = false
    }

    create("devOnlyNonPublishable") {
        description = "Runtime and compiletime dependencies that are not published alongside the jar (compileOnly + runtimeOnlyNonPublishable)"
        canBeConsumed = false
        canBeResolved = false
    }
    compileOnly.extendsFrom(devOnlyNonPublishable)
    runtimeOnlyNonPublishable.extendsFrom(devOnlyNonPublishable)
}

if (enableModernJavaSyntax.toBoolean()) {
    repositories {
        mavenCentral {
            mavenContent {
                includeGroup("me.eigenraven.java8unsupported")
            }
        }
    }

    dependencies {
        annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:1.0.0'
        // workaround for https://github.com/bsideup/jabel/issues/174
        annotationProcessor 'net.java.dev.jna:jna-platform:5.13.0'
        compileOnly('com.github.bsideup.jabel:jabel-javac-plugin:1.0.0') {
            transitive = false // We only care about the 1 annotation class
        }
        // Allow using jdk.unsupported classes like sun.misc.Unsafe in the compiled code, working around JDK-8206937.
        patchedMinecraft('me.eigenraven.java8unsupported:java-8-unsupported-shim:1.0.0')
    }

    tasks.withType(JavaCompile).configureEach {
        if (it.name in ["compileMcLauncherJava", "compilePatchedMcJava"]) {
            return
        }
        sourceCompatibility = 17 // for the IDE support
        options.release.set(8)

        javaCompiler.set(javaToolchains.compilerFor {
            languageVersion.set(JavaLanguageVersion.of(17))
            vendor.set(JvmVendorSpec.AZUL)
        })
    }
}

eclipse {
    classpath {
        downloadSources = true
        downloadJavadoc = true
    }
}

final String modGroupPath = modGroup.toString().replace('.' as char, '/' as char)
final String apiPackagePath = apiPackage.toString().replace('.' as char, '/' as char)

String targetPackageJava = javaSourceDir + modGroupPath
String targetPackageScala = scalaSourceDir + modGroupPath
String targetPackageKotlin = kotlinSourceDir + modGroupPath
if (!(getFile(targetPackageJava).exists() || getFile(targetPackageScala).exists() || getFile(targetPackageKotlin).exists())) {
    throw new GradleException("Could not resolve \"modGroup\"! Could not find " + targetPackageJava + " or " + targetPackageScala + " or " + targetPackageKotlin)
}

if (apiPackage) {
    targetPackageJava = javaSourceDir + modGroupPath + "/" + apiPackagePath
    targetPackageScala = scalaSourceDir + modGroupPath + "/" + apiPackagePath
    targetPackageKotlin = kotlinSourceDir + modGroupPath + "/" + apiPackagePath
    if (!(getFile(targetPackageJava).exists() || getFile(targetPackageScala).exists() || getFile(targetPackageKotlin).exists())) {
        throw new GradleException("Could not resolve \"apiPackage\"! Could not find " + targetPackageJava + " or " + targetPackageScala + " or " + targetPackageKotlin)
    }
}

if (accessTransformersFile) {
    for (atFile in accessTransformersFile.split(",")) {
        String targetFile = "src/main/resources/META-INF/" + atFile.trim()
        if (!getFile(targetFile).exists()) {
            throw new GradleException("Could not resolve \"accessTransformersFile\"! Could not find " + targetFile)
        }
        tasks.deobfuscateMergedJarToSrg.accessTransformerFiles.from(targetFile)
        tasks.srgifyBinpatchedJar.accessTransformerFiles.from(targetFile)
    }
} else {
    boolean atsFound = false
    for (File at : sourceSets.getByName("main").resources.files) {
        if (at.name.toLowerCase().endsWith("_at.cfg")) {
            atsFound = true
            tasks.deobfuscateMergedJarToSrg.accessTransformerFiles.from(at)
            tasks.srgifyBinpatchedJar.accessTransformerFiles.from(at)
        }
    }
    for (File at : sourceSets.getByName("api").resources.files) {
        if (at.name.toLowerCase().endsWith("_at.cfg")) {
            atsFound = true
            tasks.deobfuscateMergedJarToSrg.accessTransformerFiles.from(at)
            tasks.srgifyBinpatchedJar.accessTransformerFiles.from(at)
        }
    }
    if (atsFound) {
        logger.warn("Found and added access transformers in the resources folder, please configure gradle.properties to explicitly mention them by name")
    }
}

if (usesMixins.toBoolean()) {
    if (mixinsPackage.isEmpty()) {
        throw new GradleException("\"usesMixins\" requires \"mixinsPackage\" to be set!")
    }
    final String mixinPackagePath = mixinsPackage.toString().replaceAll("\\.", "/")
    final String mixinPluginPath = mixinPlugin.toString().replaceAll("\\.", "/")

    targetPackageJava = javaSourceDir + modGroupPath + "/" + mixinPackagePath
    targetPackageScala = scalaSourceDir + modGroupPath + "/" + mixinPackagePath
    targetPackageKotlin = kotlinSourceDir + modGroupPath + "/" + mixinPackagePath
    if (!(getFile(targetPackageJava).exists() || getFile(targetPackageScala).exists() || getFile(targetPackageKotlin).exists())) {
        throw new GradleException("Could not resolve \"mixinsPackage\"! Could not find " + targetPackageJava + " or " + targetPackageScala + " or " + targetPackageKotlin)
    }

    if (!mixinPlugin.isEmpty()) {
        String targetFileJava = javaSourceDir + modGroupPath + "/" + mixinPluginPath + ".java"
        String targetFileScala = scalaSourceDir + modGroupPath + "/" + mixinPluginPath + ".scala"
        String targetFileScalaJava = scalaSourceDir + modGroupPath + "/" + mixinPluginPath + ".java"
        String targetFileKotlin = kotlinSourceDir + modGroupPath + "/" + mixinPluginPath + ".kt"
        if (!(getFile(targetFileJava).exists() || getFile(targetFileScala).exists() || getFile(targetFileScalaJava).exists() || getFile(targetFileKotlin).exists())) {
            throw new GradleException("Could not resolve \"mixinPlugin\"! Could not find " + targetFileJava + " or " + targetFileScala + " or " + targetFileScalaJava + " or " + targetFileKotlin)
        }
    }
}

if (coreModClass) {
    final String coreModPath = coreModClass.toString().replaceAll("\\.", "/")
    String targetFileJava = javaSourceDir + modGroupPath + "/" + coreModPath + ".java"
    String targetFileScala = scalaSourceDir + modGroupPath + "/" + coreModPath + ".scala"
    String targetFileScalaJava = scalaSourceDir + modGroupPath + "/" + coreModPath + ".java"
    String targetFileKotlin = kotlinSourceDir + modGroupPath + "/" + coreModPath + ".kt"
    if (!(getFile(targetFileJava).exists() || getFile(targetFileScala).exists() || getFile(targetFileScalaJava).exists() || getFile(targetFileKotlin).exists())) {
        throw new GradleException("Could not resolve \"coreModClass\"! Could not find " + targetFileJava + " or " + targetFileScala + " or " + targetFileScalaJava + " or " + targetFileKotlin)
    }
}

configurations.configureEach {
    resolutionStrategy.cacheChangingModulesFor(0, TimeUnit.SECONDS)

    // Make sure GregTech build won't time out
    System.setProperty("org.gradle.internal.http.connectionTimeout", 120000 as String)
    System.setProperty("org.gradle.internal.http.socketTimeout", 120000 as String)
}

// Fix Jenkins' Git: chmod a file should not be detected as a change and append a '.dirty' to the version
try {
    'git config core.fileMode false'.execute()
}
catch (Exception ignored) {
    out.style(Style.Failure).println("git isn't installed at all")
}

// Pulls version first from the VERSION env and then git tag
String identifiedVersion
String versionOverride = System.getenv("VERSION") ?: null
try {
    identifiedVersion = versionOverride == null ? gitVersion() : versionOverride
}
catch (Exception ignored) {
    out.style(Style.Failure).text(
        'This mod must be version controlled by Git AND the repository must provide at least one tag,\n' +
            'or the VERSION override must be set! ').style(Style.SuccessHeader).text('(Do NOT download from GitHub using the ZIP option, instead\n' +
        'clone the repository, see ').style(Style.Info).text('https://gtnh.miraheze.org/wiki/Development').style(Style.SuccessHeader).println(' for details.)'
    )
    versionOverride = 'NO-GIT-TAG-SET'
    identifiedVersion = versionOverride
}
version = identifiedVersion
ext {
    modVersion = identifiedVersion
}

if (identifiedVersion == versionOverride) {
    out.style(Style.Failure).text('Override version to ').style(Style.Identifier).text(modVersion).style(Style.Failure).println('!\7')
}

group = "com.github.GTNewHorizons"
if (project.hasProperty("customArchiveBaseName") && customArchiveBaseName) {
    archivesBaseName = customArchiveBaseName
} else {
    archivesBaseName = modId
}


minecraft {
    if (replaceGradleTokenInFile) {
        for (f in replaceGradleTokenInFile.split(',')) {
            tagReplacementFiles.add f
        }
    }
    if (gradleTokenModId) {
        injectedTags.put gradleTokenModId, modId
    }
    if (gradleTokenModName) {
        injectedTags.put gradleTokenModName, modName
    }
    if (gradleTokenVersion) {
        injectedTags.put gradleTokenVersion, modVersion
    }
    if (gradleTokenGroupName) {
        injectedTags.put gradleTokenGroupName, modGroup
    }
    if (enableGenericInjection.toBoolean()) {
        injectMissingGenerics.set(true)
    }

    username = developmentEnvironmentUserName.toString()

    lwjgl3Version = "3.3.2"

    // Enable assertions in the current mod
    extraRunJvmArguments.add("-ea:${modGroup}")

    if (usesMixins.toBoolean() || forceEnableMixins.toBoolean()) {
        if (usesMixinDebug.toBoolean()) {
            extraRunJvmArguments.addAll([
                "-Dmixin.debug.countInjections=true",
                "-Dmixin.debug.verbose=true",
                "-Dmixin.debug.export=true"
            ])
        }
    }

    // Blowdryer is present in some old mod builds, do not propagate it further as a dependency
    // IC2 has no reobf jars in its Maven
    groupsToExcludeFromAutoReobfMapping.addAll(["com.diffplug", "com.diffplug.durian", "net.industrial-craft"])
}

if (generateGradleTokenClass) {
    tasks.injectTags.outputClassName.set(generateGradleTokenClass)
}

// Custom reobf auto-mappings
configurations.configureEach {
    dependencies.configureEach { dep ->
        if (dep instanceof org.gradle.api.artifacts.ExternalModuleDependency) {
            if (dep.group == "net.industrial-craft" && dep.name == "industrialcraft-2") {
                // https://www.curseforge.com/minecraft/mc-mods/industrial-craft/files/2353971
                project.dependencies.reobfJarConfiguration("curse.maven:ic2-242638:2353971")
            }
        }
    }
    def obfuscationAttr = it.attributes.getAttribute(ObfuscationAttribute.OBFUSCATION_ATTRIBUTE)
    if (obfuscationAttr != null && obfuscationAttr.name == ObfuscationAttribute.SRG) {
        resolutionStrategy.eachDependency { DependencyResolveDetails details ->
            // Remap CoFH core cursemaven dev jar to the obfuscated version for runObfClient/Server
            if (details.requested.group == 'curse.maven' && details.requested.name.endsWith('-69162') && details.requested.version == '2388751') {
                details.useVersion '2388750'
                details.because 'Pick obfuscated jar'
            }
        }
    }
}

// Ensure tests have access to minecraft classes
sourceSets {
    test {
        java {
            compileClasspath += sourceSets.patchedMc.output + sourceSets.mcLauncher.output
            runtimeClasspath += sourceSets.patchedMc.output + sourceSets.mcLauncher.output
        }
    }
}

if (file('addon.gradle').exists()) {
    apply from: 'addon.gradle'
}

// Allow unsafe repos but warn
repositories.configureEach { repo ->
    if (repo instanceof org.gradle.api.artifacts.repositories.UrlArtifactRepository) {
        if (repo.getUrl() != null && repo.getUrl().getScheme() == "http" && !repo.allowInsecureProtocol) {
            logger.warn("Deprecated: Allowing insecure connections for repo '${repo.name}' - add 'allowInsecureProtocol = true'")
            repo.allowInsecureProtocol = true
        }
    }
}

apply from: 'repositories.gradle'

configurations {
    runtimeClasspath.extendsFrom(runtimeOnlyNonPublishable)
    testRuntimeClasspath.extendsFrom(runtimeOnlyNonPublishable)
    for (config in [compileClasspath, runtimeClasspath, testCompileClasspath, testRuntimeClasspath]) {
        if (usesShadowedDependencies.toBoolean()) {
            config.extendsFrom(shadowImplementation)
            // TODO: remove Compile after all uses are refactored to Implementation
            config.extendsFrom(shadeCompile)
            config.extendsFrom(shadowCompile)
        }
    }
    // A "bag-of-dependencies"-style configuration for backwards compatibility, gets put in "api"
    create("compile") {
        description = "Deprecated: use api or implementation instead, gets put in api"
        canBeConsumed = false
        canBeResolved = false
        visible = false
    }
    create("testCompile") {
        description = "Deprecated: use testImplementation instead"
        canBeConsumed = false
        canBeResolved = false
        visible = false
    }
    api.extendsFrom(compile)
    testImplementation.extendsFrom(testCompile)
}

afterEvaluate {
    if (!configurations.compile.allDependencies.empty || !configurations.testCompile.allDependencies.empty) {
        logger.warn("This project uses deprecated `compile` dependencies, please migrate to using `api` and `implementation`")
        logger.warn("For more details, see https://github.com/GTNewHorizons/ExampleMod1.7.10/blob/master/dependencies.gradle")
    }
}

repositories {
    maven {
        name 'Overmind forge repo mirror'
        url 'https://gregtech.overminddl1.com/'
        mavenContent {
            excludeGroup("net.minecraftforge") // missing the `universal` artefact
        }
    }
    maven {
        name = "GTNH Maven"
        url = "http://jenkins.usrv.eu:8081/nexus/content/groups/public/"
        allowInsecureProtocol = true
    }
    maven {
        name 'sonatype'
        url 'https://oss.sonatype.org/content/repositories/snapshots/'
        content {
            includeGroup "org.lwjgl"
        }
    }
    if (includeWellKnownRepositories.toBoolean()) {
        maven {
            name "CurseMaven"
            url "https://cursemaven.com"
            content {
                includeGroup "curse.maven"
            }
        }
        maven {
            name = "ic2"
            url = getURL("https://maven.ic2.player.to/", "https://maven2.ic2.player.to/")
            content {
                includeGroup "net.industrial-craft"
            }
            metadataSources {
                mavenPom()
                artifact()
            }
        }
        maven {
            name "MMD Maven"
            url "https://maven.mcmoddev.com/"
        }
    }
}

def mixinProviderGroup = "io.github.legacymoddingmc"
def mixinProviderModule = "unimixins"
def mixinProviderVersion = "0.1.7.1"
def mixinProviderSpecNoClassifer = "${mixinProviderGroup}:${mixinProviderModule}:${mixinProviderVersion}"
def mixinProviderSpec = "${mixinProviderSpecNoClassifer}:dev"
ext.mixinProviderSpec = mixinProviderSpec

dependencies {
    if (usesMixins.toBoolean()) {
        annotationProcessor('org.ow2.asm:asm-debug-all:5.0.3')
        annotationProcessor('com.google.guava:guava:24.1.1-jre')
        annotationProcessor('com.google.code.gson:gson:2.8.6')
        annotationProcessor(mixinProviderSpec)
        if (usesMixinDebug.toBoolean()) {
            runtimeOnlyNonPublishable('org.jetbrains:intellij-fernflower:1.2.1.16')
        }
    }
    if (usesMixins.toBoolean()) {
        implementation(mixinProviderSpec)
    } else if (forceEnableMixins.toBoolean()) {
        runtimeOnlyNonPublishable(mixinProviderSpec)
    }
}

pluginManager.withPlugin('org.jetbrains.kotlin.kapt') {
    if (usesMixins.toBoolean()) {
        dependencies {
            kapt(mixinProviderSpec)
        }
    }
}

// Replace old mixin mods with unimixins
// https://docs.gradle.org/8.0.2/userguide/resolution_rules.html#sec:substitution_with_classifier
configurations.all {
    resolutionStrategy.dependencySubstitution {
        substitute module('com.gtnewhorizon:gtnhmixins') using module(mixinProviderSpecNoClassifer) withClassifier("dev") because("Unimixins replaces other mixin mods")
        substitute module('com.github.GTNewHorizons:Mixingasm') using module(mixinProviderSpecNoClassifer) withClassifier("dev") because("Unimixins replaces other mixin mods")
        substitute module('com.github.GTNewHorizons:SpongePoweredMixin') using module(mixinProviderSpecNoClassifer) withClassifier("dev") because("Unimixins replaces other mixin mods")
        substitute module('com.github.GTNewHorizons:SpongeMixins') using module(mixinProviderSpecNoClassifer) withClassifier("dev") because("Unimixins replaces other mixin mods")
        substitute module('io.github.legacymoddingmc:unimixins') using module(mixinProviderSpecNoClassifer) withClassifier("dev") because("Our previous unimixins upload was missing the dev classifier")
    }
}

apply from: 'dependencies.gradle'

def mixingConfigRefMap = 'mixins.' + modId + '.refmap.json'
def mixinTmpDir = buildDir.path + File.separator + 'tmp' + File.separator + 'mixins'
def refMap = "${mixinTmpDir}" + File.separator + mixingConfigRefMap
def mixinSrg = "${mixinTmpDir}" + File.separator + "mixins.srg"

tasks.register('generateAssets') {
    group = "GTNH Buildscript"
    description = "Generates a mixin config file at /src/main/resources/mixins.modid.json if needed"
    onlyIf { usesMixins.toBoolean() }
    doLast {
        def mixinConfigFile = getFile("/src/main/resources/mixins." + modId + ".json")
        if (!mixinConfigFile.exists()) {
            def mixinPluginLine = ""
            if (!mixinPlugin.isEmpty()) {
                // We might not have a mixin plugin if we're using early/late mixins
                mixinPluginLine += """\n  "plugin": "${modGroup}.${mixinPlugin}", """
            }

            mixinConfigFile.text = """{
  "required": true,
  "minVersion": "0.8.5-GTNH",
  "package": "${modGroup}.${mixinsPackage}",${mixinPluginLine}
  "refmap": "${mixingConfigRefMap}",
  "target": "@env(DEFAULT)",
  "compatibilityLevel": "JAVA_8",
  "mixins": [],
  "client": [],
  "server": []
}
"""
        }
    }
}

if (usesMixins.toBoolean()) {
    tasks.named("reobfJar", ReobfuscatedJar).configure {
        extraSrgFiles.from(mixinSrg)
    }

    tasks.named("processResources").configure {
        dependsOn("generateAssets")
    }

    tasks.named("compileJava", JavaCompile).configure {
        doFirst {
            new File(mixinTmpDir).mkdirs()
        }
        options.compilerArgs += [
            "-AreobfSrgFile=${tasks.reobfJar.srg.get().asFile}",
            "-AoutSrgFile=${mixinSrg}",
            "-AoutRefMapFile=${refMap}",
            // Elan: from what I understand they are just some linter configs so you get some warning on how to properly code
            "-XDenableSunApiLintControl",
            "-XDignore.symbol.file"
        ]
    }

    pluginManager.withPlugin('org.jetbrains.kotlin.kapt') {
        kapt {
            correctErrorTypes = true
            javacOptions {
                option("-AreobfSrgFile=${tasks.reobfJar.srg.get().asFile}")
                option("-AoutSrgFile=$mixinSrg")
                option("-AoutRefMapFile=$refMap")
            }
        }
        tasks.configureEach { task ->
            if (task.name == "kaptKotlin") {
                task.doFirst {
                    new File(mixinTmpDir).mkdirs()
                }
            }
        }
    }

}

tasks.named("processResources", ProcessResources).configure {
    // this will ensure that this task is redone when the versions change.
    inputs.property "version", project.version
    inputs.property "mcversion", project.minecraft.mcVersion
    exclude("spotless.gradle")

    // replace stuff in mcmod.info, nothing else. replaces ${key} with value in text
    filesMatching("mcmod.info")  {
        expand "minecraftVersion": project.minecraft.mcVersion,
            "modVersion": modVersion,
            "modId": modId,
            "modName": modName
    }

    if (usesMixins.toBoolean()) {
        from refMap
        dependsOn("compileJava", "compileScala")
    }
}

ext.java17Toolchain = (JavaToolchainSpec spec) -> {
    spec.languageVersion.set(JavaLanguageVersion.of(17))
    spec.vendor.set(JvmVendorSpec.matching("jetbrains"))
}

ext.java17DependenciesCfg = configurations.create("java17Dependencies") {
    extendsFrom(configurations.getByName("runtimeClasspath")) // Ensure consistent transitive dependency resolution
    canBeConsumed = false
}
ext.java17PatchDependenciesCfg = configurations.create("java17PatchDependencies") {
    canBeConsumed = false
}

dependencies {
    def lwjgl3ifyVersion = '1.3.5'
    def asmVersion = '9.4'
    if (modId != 'lwjgl3ify') {
        java17Dependencies("com.github.GTNewHorizons:lwjgl3ify:${lwjgl3ifyVersion}")
    }
    if (modId != 'hodgepodge') {
        java17Dependencies('com.github.GTNewHorizons:Hodgepodge:2.2.13')
    }

    java17PatchDependencies('net.minecraft:launchwrapper:1.15') {transitive = false}
    java17PatchDependencies("org.ow2.asm:asm:${asmVersion}")
    java17PatchDependencies("org.ow2.asm:asm-commons:${asmVersion}")
    java17PatchDependencies("org.ow2.asm:asm-tree:${asmVersion}")
    java17PatchDependencies("org.ow2.asm:asm-analysis:${asmVersion}")
    java17PatchDependencies("org.ow2.asm:asm-util:${asmVersion}")
    java17PatchDependencies('org.ow2.asm:asm-deprecated:7.1')
    java17PatchDependencies("org.apache.commons:commons-lang3:3.12.0")
    java17PatchDependencies("com.github.GTNewHorizons:lwjgl3ify:${lwjgl3ifyVersion}:forgePatches") {transitive = false}
}

ext.java17JvmArgs = [
    // Java 9+ support
    "--illegal-access=warn",
    "-Djava.security.manager=allow",
    "-Dfile.encoding=UTF-8",
    "--add-opens", "java.base/jdk.internal.loader=ALL-UNNAMED",
    "--add-opens", "java.base/java.net=ALL-UNNAMED",
    "--add-opens", "java.base/java.nio=ALL-UNNAMED",
    "--add-opens", "java.base/java.io=ALL-UNNAMED",
    "--add-opens", "java.base/java.lang=ALL-UNNAMED",
    "--add-opens", "java.base/java.lang.reflect=ALL-UNNAMED",
    "--add-opens", "java.base/java.text=ALL-UNNAMED",
    "--add-opens", "java.base/java.util=ALL-UNNAMED",
    "--add-opens", "java.base/jdk.internal.reflect=ALL-UNNAMED",
    "--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED",
    "--add-opens", "jdk.naming.dns/com.sun.jndi.dns=ALL-UNNAMED,java.naming",
    "--add-opens", "java.desktop/sun.awt.image=ALL-UNNAMED",
    "--add-modules", "jdk.dynalink",
    "--add-opens", "jdk.dynalink/jdk.dynalink.beans=ALL-UNNAMED",
    "--add-modules", "java.sql.rowset",
    "--add-opens", "java.sql.rowset/javax.sql.rowset.serial=ALL-UNNAMED"
]

ext.hotswapJvmArgs = [
    // DCEVM advanced hot reload
    "-XX:+AllowEnhancedClassRedefinition",
    "-XX:HotswapAgent=fatjar"
]

ext.setupHotswapAgentTask = tasks.register("setupHotswapAgent") {
    group = "GTNH Buildscript"
    description = "Installs a recent version of HotSwapAgent into the Java 17 JetBrains runtime directory"
    def hsaUrl = 'https://github.com/HotswapProjects/HotswapAgent/releases/download/1.4.2-SNAPSHOT/hotswap-agent-1.4.2-SNAPSHOT.jar'
    def targetFolderProvider = javaToolchains.launcherFor(java17Toolchain).map {it.metadata.installationPath.dir("lib/hotswap")}
    def targetFilename = "hotswap-agent.jar"
    onlyIf {
        !targetFolderProvider.get().file(targetFilename).asFile.exists()
    }
    doLast {
        def targetFolder = targetFolderProvider.get()
        targetFolder.asFile.mkdirs()
        download.run {
            src hsaUrl
            dest targetFolder.file(targetFilename).asFile
            overwrite false
            tempAndMove true
        }
    }
}

public abstract class RunHotswappableMinecraftTask extends RunMinecraftTask {
    // IntelliJ doesn't seem to allow commandline arguments so we also support an env variable
    private boolean enableHotswap = Boolean.valueOf(System.getenv("HOTSWAP"));

    @Input
    public boolean getEnableHotswap() { return enableHotswap }
    @Option(option = "hotswap", description = "Enables HotSwapAgent for enhanced class reloading under a debugger")
    public boolean setEnableHotswap(boolean enable) { enableHotswap = enable }

    @Inject
    public RunHotswappableMinecraftTask(Distribution side, String superTask, org.gradle.api.invocation.Gradle gradle) {
        super(side, gradle)

        this.lwjglVersion = 3
        this.javaLauncher = project.javaToolchains.launcherFor(project.java17Toolchain)
        this.extraJvmArgs.addAll(project.java17JvmArgs)
        this.extraJvmArgs.addAll(project.provider(() -> enableHotswap ? project.hotswapJvmArgs : []))

        this.classpath(project.java17PatchDependenciesCfg)
        if (side == Distribution.CLIENT) {
            this.classpath(project.minecraftTasks.lwjgl3Configuration)
        }
        // Use a raw provider instead of map to not create a dependency on the task
        this.classpath(project.provider(() -> project.tasks.named(superTask, RunMinecraftTask).get().classpath))
        this.classpath.filter { file ->
            !file.path.contains("2.9.4-nightly-20150209") // Remove lwjgl2
        }
        this.classpath(project.java17DependenciesCfg)
    }

    public void setup(Project project) {
        super.setup(project)
        if (project.usesMixins.toBoolean()) {
            this.extraJvmArgs.addAll(project.provider(() -> {
                def mixinCfg = project.configurations.detachedConfiguration(project.dependencies.create(project.mixinProviderSpec))
                mixinCfg.canBeConsumed = false
                mixinCfg.transitive = false
                enableHotswap ? ["-javaagent:" + mixinCfg.singleFile.absolutePath] : []
            }))
        }
    }
}

def runClient17Task = tasks.register("runClient17", RunHotswappableMinecraftTask, Distribution.CLIENT, "runClient")
runClient17Task.configure {
    setup(project)
    group = "Modded Minecraft"
    description = "Runs the modded client using Java 17, lwjgl3ify and Hodgepodge"
    dependsOn(setupHotswapAgentTask, mcpTasks.launcherSources.classesTaskName, minecraftTasks.taskDownloadVanillaAssets, mcpTasks.taskPackagePatchedMc, 'jar')
    mainClass = "GradleStart"
    username = minecraft.username
    userUUID = minecraft.userUUID
}

def runServer17Task = tasks.register("runServer17", RunHotswappableMinecraftTask, Distribution.DEDICATED_SERVER, "runServer")
runServer17Task.configure {
    setup(project)
    group = "Modded Minecraft"
    description = "Runs the modded server using Java 17, lwjgl3ify and Hodgepodge"
    dependsOn(setupHotswapAgentTask, mcpTasks.launcherSources.classesTaskName, minecraftTasks.taskDownloadVanillaAssets, mcpTasks.taskPackagePatchedMc, 'jar')
    mainClass = "GradleStartServer"
    extraArgs.add("nogui")
}

def getManifestAttributes() {
    def manifestAttributes = [:]
    if (!containsMixinsAndOrCoreModOnly.toBoolean() && (usesMixins.toBoolean() || coreModClass)) {
        manifestAttributes += ["FMLCorePluginContainsFMLMod": true]
    }

    if (accessTransformersFile) {
        manifestAttributes += ["FMLAT": accessTransformersFile.toString()]
    }

    if (coreModClass) {
        manifestAttributes += ["FMLCorePlugin": modGroup + "." + coreModClass]
    }

    if (usesMixins.toBoolean()) {
        manifestAttributes += [
            "TweakClass"    : "org.spongepowered.asm.launch.MixinTweaker",
            "MixinConfigs"  : "mixins." + modId + ".json",
            "ForceLoadAsMod": !containsMixinsAndOrCoreModOnly.toBoolean()
        ]
    }
    return manifestAttributes
}

tasks.named("jar", Jar).configure {
    manifest {
        attributes(getManifestAttributes())
    }
}

if (usesShadowedDependencies.toBoolean()) {
    tasks.named("shadowJar", ShadowJar).configure {
        manifest {
            attributes(getManifestAttributes())
        }

        if (minimizeShadowedDependencies.toBoolean()) {
            minimize()  // This will only allow shading for actually used classes
        }
        configurations = [
            project.configurations.shadowImplementation,
            project.configurations.shadowCompile,
            project.configurations.shadeCompile
        ]
        archiveClassifier.set('dev')
        if (relocateShadowedDependencies.toBoolean()) {
            relocationPrefix = modGroup + ".shadow"
            enableRelocation = true
        }
    }
    configurations.runtimeElements.outgoing.artifacts.clear()
    configurations.apiElements.outgoing.artifacts.clear()
    configurations.runtimeElements.outgoing.artifact(tasks.named("shadowJar", ShadowJar))
    configurations.apiElements.outgoing.artifact(tasks.named("shadowJar", ShadowJar))
    tasks.named("jar", Jar) {
        enabled = false
        finalizedBy(tasks.shadowJar)
    }
    tasks.named("reobfJar", ReobfuscatedJar) {
        inputJar.set(tasks.named("shadowJar", ShadowJar).flatMap({it.archiveFile}))
    }
    AdhocComponentWithVariants javaComponent = (AdhocComponentWithVariants) project.components.findByName("java")
    javaComponent.withVariantsFromConfiguration(configurations.shadowRuntimeElements) {
        skip()
    }
    for (runTask in ["runClient", "runServer", "runClient17", "runServer17"]) {
        tasks.named(runTask).configure {
            dependsOn("shadowJar")
        }
    }
}
ext.publishableDevJar = usesShadowedDependencies.toBoolean() ? tasks.shadowJar : tasks.jar
ext.publishableObfJar = tasks.reobfJar

tasks.register('apiJar', Jar) {
    from(sourceSets.main.allSource) {
        include modGroupPath + "/" + apiPackagePath + '/**'
    }

    from(sourceSets.main.output) {
        include modGroupPath + "/" + apiPackagePath + '/**'
    }

    from(sourceSets.main.resources.srcDirs) {
        include("LICENSE")
    }

    getArchiveClassifier().set('api')
}

artifacts {
    if (!noPublishedSources) {
        archives tasks.named("sourcesJar")
    }
    if (apiPackage) {
        archives tasks.named("apiJar")
    }
}

idea {
    module {
        downloadJavadoc = true
        downloadSources = true
        inheritOutputDirs = true
    }
    project {
        settings {
            if (ideaOverrideBuildType != "") {
                delegateActions {
                    if ("gradle".equalsIgnoreCase(ideaOverrideBuildType)) {
                        delegateBuildRunToGradle = true
                        testRunner = org.jetbrains.gradle.ext.ActionDelegationConfig.TestRunner.GRADLE
                    } else if ("idea".equalsIgnoreCase(ideaOverrideBuildType)) {
                        delegateBuildRunToGradle = false
                        testRunner = org.jetbrains.gradle.ext.ActionDelegationConfig.TestRunner.PLATFORM
                    } else {
                        throw GradleScriptException('Accepted value for ideaOverrideBuildType is one of gradle or idea.')
                    }
                }
            }
            runConfigurations {
                "1. Run Client"(Gradle) {
                    taskNames = ["runClient"]
                }
                "2. Run Server"(Gradle) {
                    taskNames = ["runServer"]
                }
                "1a. Run Client (Java 17)"(Gradle) {
                    taskNames = ["runClient17"]
                }
                "2a. Run Server (Java 17)"(Gradle) {
                    taskNames = ["runServer17"]
                }
                "1b. Run Client (Java 17, Hotswap)"(Gradle) {
                    taskNames = ["runClient17"]
                    envs = ["HOTSWAP": "true"]
                }
                "2b. Run Server (Java 17, Hotswap)"(Gradle) {
                    taskNames = ["runServer17"]
                    envs = ["HOTSWAP": "true"]
                }
                "3. Run Obfuscated Client"(Gradle) {
                    taskNames = ["runObfClient"]
                }
                "4. Run Obfuscated Server"(Gradle) {
                    taskNames = ["runObfServer"]
                }
                if (!disableSpotless) {
                    "5. Apply spotless"(Gradle) {
                        taskNames = ["spotlessApply"]
                    }
                }
                def coreModArgs = ""
                if (coreModClass) {
                    coreModArgs = ' "-Dfml.coreMods.load=' + modGroup + '.' + coreModClass + '"'
                }
                "Run Client (IJ Native)"(Application) {
                    mainClass = "GradleStart"
                    moduleName = project.name + ".ideVirtualMain"
                    afterEvaluate {
                        workingDirectory = tasks.runClient.workingDir.absolutePath
                        programParameters = tasks.runClient.calculateArgs(project).collect { '"' + it + '"' }.join(' ')
                        jvmArgs = tasks.runClient.calculateJvmArgs(project).collect { '"' + it + '"' }.join(' ') +
                            ' ' + tasks.runClient.systemProperties.collect { '"-D' + it.key + '=' + it.value.toString() + '"' }.join(' ') +
                            coreModArgs
                    }
                }
                "Run Server (IJ Native)"(Application) {
                    mainClass = "GradleStartServer"
                    moduleName = project.name + ".ideVirtualMain"
                    afterEvaluate {
                        workingDirectory = tasks.runServer.workingDir.absolutePath
                        programParameters = tasks.runServer.calculateArgs(project).collect { '"' + it + '"' }.join(' ')
                        jvmArgs = tasks.runServer.calculateJvmArgs(project).collect { '"' + it + '"' }.join(' ') +
                            ' ' + tasks.runServer.systemProperties.collect { '"-D' + it.key + '=' + it.value.toString() + '"' }.join(' ') +
                            coreModArgs
                    }
                }
            }
            compiler.javac {
                afterEvaluate {
                    javacAdditionalOptions = "-encoding utf8"
                    moduleJavacAdditionalOptions = [
                        (project.name + ".main"): tasks.compileJava.options.compilerArgs.collect { '"' + it + '"' }.join(' ')
                    ]
                }
            }
            withIDEADir { File ideaDir ->
                if (!ideaDir.path.contains(".idea")) {
                    // If an .ipr file exists, the project root directory is passed here instead of the .idea subdirectory
                    ideaDir = new File(ideaDir, ".idea")
                }
                if (ideaDir.isDirectory()) {
                    def miscFile = new File(ideaDir, "misc.xml")
                    if (miscFile.isFile()) {
                        boolean dirty = false
                        def miscTransformer = new XmlTransformer()
                        miscTransformer.addAction { root ->
                            Node rootNode = root.asNode()
                            def rootManager = rootNode
                                .component.find { it.@name == 'ProjectRootManager' }
                            if (!rootManager) {
                                rootManager = rootNode.appendNode('component', ['name': 'ProjectRootManager', 'version': '2'])
                                dirty = true
                            }
                            def output = rootManager.output
                            if (!output) {
                                output = rootManager.appendNode('output')
                                dirty = true
                            }
                            if (!output.@url) {
                                // Only modify the output url if it doesn't yet have one, or if the existing one is blank somehow.
                                // This is a sensible default for most setups
                                output.@url = 'file://$PROJECT_DIR$/build/ideaBuild'
                                dirty = true
                            }
                        }
                        def result = miscTransformer.transform(miscFile.text)
                        if (dirty) {
                            miscFile.write(result)
                        }
                    } else {
                        miscFile.text = """<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ProjectRootManager" version="2">
    <output url="file://\$PROJECT_DIR\$/out" />
  </component>
</project>
"""
                    }
                }
            }
        }
    }
}

tasks.named("processIdeaSettings").configure {
    dependsOn("injectTags")
}

// workaround variable hiding in pom processing
def projectConfigs = project.configurations

publishing {
    publications {
        create("maven", MavenPublication) {
            from components.java

            if (apiPackage) {
                artifact apiJar
            }

            groupId = System.getenv("ARTIFACT_GROUP_ID") ?: project.group
            artifactId = System.getenv("ARTIFACT_ID") ?: project.name
            // Using the identified version, not project.version as it has the prepended 1.7.10
            version = System.getenv("RELEASE_VERSION") ?: identifiedVersion
        }
    }

    repositories {
        maven {
            url = "http://jenkins.usrv.eu:8081/nexus/content/repositories/releases"
            allowInsecureProtocol = true
            credentials {
                username = System.getenv("MAVEN_USER") ?: "NONE"
                password = System.getenv("MAVEN_PASSWORD") ?: "NONE"
            }
        }
    }
}

if (modrinthProjectId.size() != 0 && System.getenv("MODRINTH_TOKEN") != null) {
    apply plugin: 'com.modrinth.minotaur'

    File changelogFile = new File(System.getenv("CHANGELOG_FILE") ?: "CHANGELOG.md")

    modrinth {
        token = System.getenv("MODRINTH_TOKEN")
        projectId = modrinthProjectId
        versionNumber = identifiedVersion
        versionType = identifiedVersion.endsWith("-pre") ? "beta" : "release"
        changelog = changelogFile.exists() ? changelogFile.getText("UTF-8") : ""
        uploadFile = publishableObfJar
        additionalFiles = getSecondaryArtifacts()
        gameVersions = [minecraftVersion]
        loaders = ["forge"]
        debugMode = false
    }

    if (modrinthRelations.size() != 0) {
        String[] deps = modrinthRelations.split(";")
        deps.each { dep ->
            if (dep.size() == 0) {
                return
            }
            String[] parts = dep.split(":")
            String[] qual = parts[0].split("-")
            addModrinthDep(qual[0], qual[1], parts[1])
        }
    }
    if (usesMixins.toBoolean()) {
        addModrinthDep("required", "project", "unimixins")
    }
    tasks.modrinth.dependsOn(build)
    tasks.publish.dependsOn(tasks.modrinth)
}

if (curseForgeProjectId.size() != 0 && System.getenv("CURSEFORGE_TOKEN") != null) {
    apply plugin: 'com.matthewprenger.cursegradle'

    File changelogFile = new File(System.getenv("CHANGELOG_FILE") ?: "CHANGELOG.md")

    curseforge {
        apiKey = System.getenv("CURSEFORGE_TOKEN")
        project {
            id = curseForgeProjectId
            if (changelogFile.exists()) {
                changelogType = "markdown"
                changelog = changelogFile
            }
            releaseType = identifiedVersion.endsWith("-pre") ? "beta" : "release"
            addGameVersion minecraftVersion
            addGameVersion "Forge"
            mainArtifact publishableObfJar
            for (artifact in getSecondaryArtifacts()) addArtifact artifact
        }

        options {
            javaIntegration = false
            forgeGradleIntegration = false
            debug = false
        }
    }

    if (curseForgeRelations.size() != 0) {
        String[] deps = curseForgeRelations.split(";")
        deps.each { dep ->
            if (dep.size() == 0) {
                return
            }
            String[] parts = dep.split(":")
            addCurseForgeRelation(parts[0], parts[1])
        }
    }
    if (usesMixins.toBoolean()) {
        addCurseForgeRelation("requiredDependency", "unimixins")
    }
    tasks.curseforge.dependsOn(build)
    tasks.publish.dependsOn(tasks.curseforge)
}

def addModrinthDep(String scope, String type, String name) {
    com.modrinth.minotaur.dependencies.Dependency dep;
    if (!(scope in ["required", "optional", "incompatible", "embedded"])) {
        throw new Exception("Invalid modrinth dependency scope: " + scope)
    }
    switch (type) {
        case "project":
            dep = new ModDependency(name, scope)
            break
        case "version":
            dep = new VersionDependency(name, scope)
            break
        default:
            throw new Exception("Invalid modrinth dependency type: " + type)
    }
    project.modrinth.dependencies.add(dep)
}

def addCurseForgeRelation(String type, String name) {
    if (!(type in ["requiredDependency", "embeddedLibrary", "optionalDependency", "tool", "incompatible"])) {
        throw new Exception("Invalid CurseForge relation type: " + type)
    }
    CurseArtifact artifact = project.curseforge.curseProjects[0].mainArtifact
    CurseRelation rel = (artifact.curseRelations ?: (artifact.curseRelations = new CurseRelation()))
    rel."$type"(name)
}

// Updating

def buildscriptGradleVersion = "8.1.1"

tasks.named('wrapper', Wrapper).configure {
    gradleVersion = buildscriptGradleVersion
}

tasks.register('updateBuildScript') {
    group = 'GTNH Buildscript'
    description = 'Updates the build script to the latest version'

    if (gradle.gradleVersion != buildscriptGradleVersion && !Boolean.getBoolean('DISABLE_BUILDSCRIPT_GRADLE_UPDATE')) {
        dependsOn('wrapper')
    }

    doLast {
        if (performBuildScriptUpdate()) return

        print("Build script already up-to-date!")
    }
}

if (!project.getGradle().startParameter.isOffline() && !Boolean.getBoolean('DISABLE_BUILDSCRIPT_UPDATE_CHECK') && isNewBuildScriptVersionAvailable()) {
    if (autoUpdateBuildScript.toBoolean()) {
        performBuildScriptUpdate()
    } else {
        out.style(Style.SuccessHeader).println("Build script update available! Run 'gradle updateBuildScript'")
        if (gradle.gradleVersion != buildscriptGradleVersion) {
            out.style(Style.SuccessHeader).println("updateBuildScript can update gradle from ${gradle.gradleVersion} to ${buildscriptGradleVersion}\n")
        }
    }
}

// If you want to add more cases to this task, implement them as arguments if total amount to print gets too large
tasks.register('faq') {
    group = 'GTNH Buildscript'
    description = 'Prints frequently asked questions about building a project'

    doLast {
        print("If your build fails to fetch dependencies, run './gradlew updateDependencies'. " +
            "Or you can manually check if the versions are still on the distributing sites - " +
            "the links can be found in repositories.gradle and build.gradle:repositories, " +
            "but not build.gradle:buildscript.repositories - those ones are for gradle plugin metadata.\n\n" +
            "If your build fails to recognize the syntax of new Java versions, enable Jabel in your " +
            "gradle.properties. See how it's done in GTNH ExampleMod/gradle.properties. " +
            "However, keep in mind that Jabel enables only syntax features, but not APIs that were introduced in " +
            "Java 9 or later.")
    }
}

static URL availableBuildScriptUrl() {
    new URL("https://raw.githubusercontent.com/GTNewHorizons/ExampleMod1.7.10/master/build.gradle.kts")
}

static URL exampleSettingsGradleUrl() {
    new URL("https://raw.githubusercontent.com/GTNewHorizons/ExampleMod1.7.10/master/settings.gradle.example")
}

static URL exampleGitAttributesUrl() {
    new URL("https://raw.githubusercontent.com/GTNewHorizons/ExampleMod1.7.10/master/.gitattributes")
}


boolean verifyGitAttributes() {
    def gitattributesFile = getFile(".gitattributes")
    if (!gitattributesFile.exists()) {
        println("Downloading default .gitattributes")
        exampleGitAttributesUrl().withInputStream { i -> gitattributesFile.withOutputStream { it << i } }
        exec {
            workingDir '.'
            commandLine 'git', 'add', '--renormalize', '.'
        }
        return true
    }
    return false
}

boolean verifySettingsGradle() {
    def settingsFile = getFile("settings.gradle")
    if (!settingsFile.exists()) {
        println("Downloading default settings.gradle")
        exampleSettingsGradleUrl().withInputStream { i -> settingsFile.withOutputStream { it << i } }
        return true
    }
    return false
}

boolean performBuildScriptUpdate() {
    if (isNewBuildScriptVersionAvailable()) {
        def buildscriptFile = getFile("build.gradle")
        availableBuildScriptUrl().withInputStream { i -> buildscriptFile.withOutputStream { it << i } }
        def out = services.get(StyledTextOutputFactory).create('buildscript-update-output')
        out.style(Style.Success).print("Build script updated. Please REIMPORT the project or RESTART your IDE!")
        boolean settingsupdated = verifySettingsGradle()
        settingsupdated = verifyGitAttributes() || settingsupdated
        if (settingsupdated)
            throw new GradleException("Settings has been updated, please re-run task.")
        return true
    }
    return false
}

boolean isNewBuildScriptVersionAvailable() {
    Map parameters = ["connectTimeout": 2000, "readTimeout": 2000]

    String currentBuildScript = getFile("build.gradle").getText()
    String currentBuildScriptHash = getVersionHash(currentBuildScript)
    String availableBuildScript = availableBuildScriptUrl().newInputStream(parameters).getText()
    String availableBuildScriptHash = getVersionHash(availableBuildScript)

    boolean isUpToDate = currentBuildScriptHash.empty || availableBuildScriptHash.empty || currentBuildScriptHash == availableBuildScriptHash
    return !isUpToDate
}

static String getVersionHash(String buildScriptContent) {
    String versionLine = buildScriptContent.find("^//version: [a-z0-9]*")
    if (versionLine != null) {
        return versionLine.split(": ").last()
    }
    return ""
}

// Parameter Deobfuscation

tasks.register('deobfParams') {
    group = 'GTNH Buildscript'
    description = 'Rename all obfuscated parameter names inherited from Minecraft classes'
    doLast { // TODO

        String mcpDir = "$project.gradle.gradleUserHomeDir/caches/minecraft/de/oceanlabs/mcp/mcp_$channel/$mappingsVersion"
        String mcpZIP = "$mcpDir/mcp_$channel-$mappingsVersion-${minecraftVersion}.zip"
        String paramsCSV = "$mcpDir/params.csv"

        download.run {
            src "https://maven.minecraftforge.net/de/oceanlabs/mcp/mcp_$channel/$mappingsVersion-$minecraftVersion/mcp_$channel-$mappingsVersion-${minecraftVersion}.zip"
            dest mcpZIP
            overwrite false
        }

        if (!file(paramsCSV).exists()) {
            println("Extracting MCP archive ...")
            copy {
                from(zipTree(mcpZIP))
                into(mcpDir)
            }
        }

        println("Parsing params.csv ...")
        Map<String, String> params = new HashMap<>()
        Files.lines(Paths.get(paramsCSV)).forEach { line ->
            String[] cells = line.split(",")
            if (cells.length > 2 && cells[0].matches("p_i?\\d+_\\d+_")) {
                params.put(cells[0], cells[1])
            }
        }

        out.style(Style.Success).println("Modified ${replaceParams(file("$projectDir/src/main/java"), params)} files!")
        out.style(Style.Failure).println("Don't forget to verify that the code still works as before!\n It could be broken due to duplicate variables existing now\n or parameters taking priority over other variables.")
    }
}

static int replaceParams(File file, Map<String, String> params) {
    int fileCount = 0

    if (file.isDirectory()) {
        for (File f : file.listFiles()) {
            fileCount += replaceParams(f, params)
        }
        return fileCount
    }
    println("Visiting ${file.getName()} ...")
    try {
        String content = new String(Files.readAllBytes(file.toPath()))
        int hash = content.hashCode()
        params.forEach { key, value ->
            content = content.replaceAll(key, value)
        }
        if (hash != content.hashCode()) {
            Files.write(file.toPath(), content.getBytes("UTF-8"))
            return 1
        }
    } catch (Exception e) {
        e.printStackTrace()
    }
    return 0
}

// Dependency Deobfuscation (Deprecated, use the new RFG API documented in dependencies.gradle)

def deobf(String sourceURL) {
    try {
        URL url = new URL(sourceURL)
        String fileName = url.getFile()

        //get rid of directories:
        int lastSlash = fileName.lastIndexOf("/")
        if (lastSlash > 0) {
            fileName = fileName.substring(lastSlash + 1)
        }
        //get rid of extension:
        if (fileName.endsWith(".jar") || fileName.endsWith(".litemod")) {
            fileName = fileName.substring(0, fileName.lastIndexOf("."))
        }

        String hostName = url.getHost()
        if (hostName.startsWith("www.")) {
            hostName = hostName.substring(4)
        }
        List parts = Arrays.asList(hostName.split("\\."))
        Collections.reverse(parts)
        hostName = String.join(".", parts)

        return deobf(sourceURL, "$hostName/$fileName")
    } catch (Exception ignored) {
        return deobf(sourceURL, "deobf/${sourceURL.hashCode()}")
    }
}

def deobfMaven(String repoURL, String mavenDep) {
    if (!repoURL.endsWith("/")) {
        repoURL += "/"
    }
    String[] parts = mavenDep.split(":")
    parts[0] = parts[0].replace('.', '/')
    def jarURL = repoURL + parts[0] + "/" + parts[1] + "/" + parts[2] + "/" + parts[1] + "-" + parts[2] + ".jar"
    return deobf(jarURL)
}

def deobfCurse(String curseDep) {
    return dependencies.rfg.deobf("curse.maven:$curseDep")
}

// The method above is to be preferred. Use this method if the filename is not at the end of the URL.
def deobf(String sourceURL, String rawFileName) {
    String bon2Version = "2.5.1"
    String fileName = URLDecoder.decode(rawFileName, "UTF-8")
    String cacheDir = "$project.gradle.gradleUserHomeDir/caches"
    String obfFile = "$cacheDir/modules-2/files-2.1/${fileName}.jar"

    download.run {
        src sourceURL
        dest obfFile
        quiet true
        overwrite false
    }
    return dependencies.rfg.deobf(files(obfFile))
}
// Helper methods

def checkPropertyExists(String propertyName) {
    if (!project.hasProperty(propertyName)) {
        throw new GradleException("This project requires a property \"" + propertyName + "\"! Please add it your \"gradle.properties\". You can find all properties and their description here: https://github.com/GTNewHorizons/ExampleMod1.7.10/blob/main/gradle.properties")
    }
}

def propertyDefaultIfUnset(String propertyName, defaultValue) {
    if (!project.hasProperty(propertyName) || project.property(propertyName) == "") {
        project.ext.setProperty(propertyName, defaultValue)
    }
}

def getFile(String relativePath) {
    return new File(projectDir, relativePath)
}

def getSecondaryArtifacts() {
    // Because noPublishedSources from the beginning of the script is somehow not visible here...
    boolean noPublishedSources = project.hasProperty("noPublishedSources") ? project.noPublishedSources.toBoolean() : false
    def secondaryArtifacts = [publishableDevJar]
    if (!noPublishedSources) secondaryArtifacts += [sourcesJar]
    if (apiPackage) secondaryArtifacts += [apiJar]
    return secondaryArtifacts
}

def getURL(String main, String fallback) {
    return pingURL(main, 10000) ? main : fallback
}

// credit: https://stackoverflow.com/a/3584332
def pingURL(String url, int timeout) {
    url = url.replaceFirst("^https", "http") // Otherwise an exception may be thrown on invalid SSL certificates.
    try {
        HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection()
        connection.setConnectTimeout(timeout)
        connection.setReadTimeout(timeout)
        connection.setRequestMethod("HEAD")
        int responseCode = connection.getResponseCode()
        return 200 <= responseCode && responseCode <= 399
    } catch (IOException ignored) {
        return false
    }
}