import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import com.modrinth.minotaur.ModrinthExtension
import com.modrinth.minotaur.request.VersionType
import com.modrinth.minotaur.dependencies.DependencyType
import net.fabricmc.loom.LoomGradleExtension
import net.fabricmc.loom.LoomGradlePlugin

buildscript {
    dependencies {
        classpath("com.guardsquare:proguard-gradle:[7.1.0-beta3, 7.3[") {
            exclude("com.android.tools.build")
        }
    }
}

plugins {
    kotlin("jvm") version "1.7.0-RC2"
    kotlin("plugin.serialization") version "1.7.0-RC"
    id("com.github.johnrengelman.shadow") version "7.1.2" apply false
    id("architectury-plugin") version "[3.4.124, 3.5["
    id("dev.architectury.loom") version (
        JavaVersion.current().let { version ->
            if (version >= JavaVersion.VERSION_16)
                "[0.9.0.153, ${if (version.isCompatibleWith(JavaVersion.VERSION_17)) "0.12.1" else "0.10.1"}["
            else "[0.7.3.152, 0.7.3.201["
        }
    ) apply false
    id("com.github.ben-manes.versions") version "0.42.0"
    id("net.kyori.indra.git") version "2.1.1"
    id("org.cqfn.diktat.diktat-gradle-plugin") version "1.1.0"
    id("com.modrinth.minotaur") version "2.2.0" apply false
}

group = "fr.username404"
version = "0.3.4"
val groupAndName = "${rootProject.group}.${rootProject.name.toLowerCase()}"

val javaVer: String = "8"
val kotlinVer: String by rootProject
val kotlinSplitVersion = kotlinVer.split('.')
val serializationVer: String by rootProject
val mcBase: String = (rootProject.property("minecraft") as String).also {
    architectury { minecraft = it }
}.substring(0..3)
val kotlinX: String = "org.jetbrains.kotlinx"

subprojects {
    group = rootProject.group.toString()
    apply(plugin = "org.cqfn.diktat.diktat-gradle-plugin")
    diktat {
        inputs { include("src/**/*.kt") }
        diktatConfigFile = file("$rootDir/Formatting.yml")
        ignoreFailures = true
    }
    lateinit var mappingsDep: Dependency
    apply(plugin = "dev.architectury.loom")
    apply(plugin = "org.jetbrains.kotlin.plugin.serialization")
    extensions.configure<LoomGradleExtension>("loom") {
        mappingsDep = layered {
            officialMojangMappings().parchment("org.parchmentmc.data:parchment-${rootProject.architectury.minecraft}:2021.10.17")
        }
        silentMojangMappingsLicense()
        val refmap = "snowygui-${project.name}-refmap.json"
        // The following is used to make this buildscript compatible with architectury-loom 0.7.3, 0.9.X, 0.10.X and higher versions
        with(javaClass) {
            val loomMajorRelease = LoomGradlePlugin.LOOM_VERSION.run {
                val numberPos = indexOf('.') + 1
                substring(numberPos, indexOf('.', numberPos))
            }.toShort()
            if (loomMajorRelease < 10) {
                if (loomMajorRelease < 9) getField("refmapName").set(this@configure, refmap)
                else getMethod("setRefmapName", String::class.java).invoke(this@configure, refmap)
                getMethod("mixinConfig", Array<String>::class.java).invoke(this@configure, arrayOf("snowygui-${project.name}.mixins.json"))
            } else {
                getDeclaredMethod("getMixin").invoke(this@configure).run {
                    javaClass.getDeclaredMethod("getDefaultRefmapName").invoke(this).let { refmapProperty ->
                        refmapProperty.javaClass.getDeclaredMethod("set", Any::class.java).invoke(refmapProperty, refmap)
                    }
                    if (isForge) {
                        this@with.getDeclaredMethod("getForge").invoke(this@configure).let { forgeObject ->
                            forgeObject.javaClass.getMethod("mixinConfigs", Array<String>::class.java).invoke(forgeObject, arrayOf("snowygui-common.mixins.json", "snowygui-forge.mixins.json"))
                        }
                    }
                }
            }
        }
    }
    apply(plugin = "com.github.johnrengelman.shadow")
    val shadowC by configurations.creating
    repositories {
        maven(url = "https://jitpack.io"); mavenCentral()
        maven(url = "https://maven.parchmentmc.org")
    }
    dependencies {
        implementation("$kotlinX:kotlinx-coroutines-jdk8:${rootProject.property("kotlin_coroutines_version")}")
        implementation("$kotlinX:kotlinx-serialization-core:$serializationVer")
        implementation("$kotlinX:kotlinx-serialization-json:$serializationVer")
        listOf(
            "com.typesafe:config:1.4.2",
            "io.github.config4k:config4k:0.4.2",
            "com.github.Vatuu:discord-rpc:1.6.2"
        ).forEach { implementation(it); shadowC(it) { isTransitive = false; exclude("com.sun.jna") } }
        "minecraft"("com.mojang:minecraft:${rootProject.property("minecraft")}")
        "mappings"(mappingsDep)
    }
    apply(plugin = "com.modrinth.minotaur")
    extensions.configure<ModrinthExtension>("modrinth") {
        projectId.set("OuGyGg6A")
        syncBodyFrom.set("$rootDir/README.md")
        gameVersions.add(mcBase)
        if (project.name == "forge") dependencies.add(com.modrinth.minotaur.dependencies.VersionDependency("Bxm9xbNJ", DependencyType.REQUIRED)) // Kotlinforforge dependency
        versionNumber.set(rootProject.version.toString() + "-${project.name}")
        versionType.set(VersionType.ALPHA.name)
        detectLoaders.set(false)
        loaders.add(project.name)
        versionName.set("${rootProject.name} ${rootProject.version}")
        uploadFile.set(fileTree("$rootDir/remappedJars/").files.find { it.name.contains(project.name) })
    }
    tasks {
        apply(plugin = "net.kyori.indra.git")
        withType(Jar::class) {
            from("$rootDir/LICENSE.txt")
            indraGit.applyVcsInformationToManifest(manifest)
            archiveBaseName.set(rootProject.name)
        }
        withType(ShadowJar::class) {
            this.configurations = listOf(shadowC)
            relocate("com.typesafe.config", "${rootProject.group}.typesafe.config")
            relocate("io.github.config4k", "${rootProject.group}.config4k")
            relocate("net.arikia.dev.drpc", "${rootProject.group}.drpc")
            exclude("com/sun/jna/**/*") // The shadowed JNA from discord-rpc isn't needed since it is already available at runtime
            exclude("**/*.kotlin_metadata")
            exclude("**/*.kotlin_builtins")
            exclude("META-INF/maven/**/*")
            archiveClassifier.set("shadow-${this@subprojects.name}")
        }
        val shadowJar = getByName("shadowJar") as ShadowJar
        val shrinkJar = register("shrinkJar", proguard.gradle.ProGuardTask::class) { group = this@subprojects.group as String
            val dictionariesDir = "$rootDir/obfuscation"
            dependsOn(shadowJar)
            injars(shadowJar)
            outjars("$buildDir/shrinkedJar/${shadowJar.outputs.files.singleFile.name}")
            keep("class $group.snowygui.mixins.* { * ; }")
            keep("class $group.snowygui.fabric.FabricInit")
            keep("class $group.snowygui.fabric.ModMenuConf")
            keep("class $group.snowygui.forge.ForgeInit")
            keepnames("class $group.snowygui.Snowy")
            keepnames("class $group.snowygui.gui.feature.Macro { <fields>; }")
            keep("class $group.snowygui.gui.feature.* { public static *** INSTANCE; }")
            keepclassmembers("class $group.snowygui.** { public protected <methods>; }")
            keepattributes("*Annotation*, Signature, InnerClasses, EnclosingMethod, MethodParameters, Synthetic, Exceptions")
            obfuscationdictionary("$dictionariesDir/dictionary.txt")
            classobfuscationdictionary("$dictionariesDir/classdictionary.txt")
            packageobfuscationdictionary("$dictionariesDir/packagesdictionary.txt")
            flattenpackagehierarchy("$group.snowygui")
            allowaccessmodification()
            adaptclassstrings()
            "$group.**".also { dontnote(it); dontwarn(it) }

            // kotlinx-serialization related configuration:
            keep(mapOf("includedescriptorclasses" to true), "class $group.snowygui.**$\$serializer { * ; }")
            keepclassmembers("class $group.snowygui.** { *** Companion; }")
            keepclasseswithmembers("class $group.snowygui.** { kotlinx.serialization.KSerializer serializer(...); }")

            // Required for discord-rpc
            keepclassmembers("class $group.drpc.** { public * ; }")

            doFirst {
                libraryjars(configurations.compileClasspath.get().filterNot { file ->
                    shadowC.contains(file)
                })
            }
            val homeDir = System.getProperty("java.home") as String
            if (JavaVersion.current().isJava9Compatible) {
                val jmodsLocations = setOf(
                    "$homeDir/jmods/java.base.jmod",
                    "$homeDir/jmods/java.desktop.jmod",
                    "$homeDir/jmods/java.instrument.jmod"
                )
                if (jmodsLocations.all { !file(it).exists() }) throw GradleException("Jmods appear to be missing, please make sure that jmods are installed.")
                else jmodsLocations.forEach {
                    libraryjars(it)
                }
            } else libraryjars("$homeDir/lib/rt.jar")
            // Note: dontpreverify() should NOT be used, it will cause errors at runtime
            useuniqueclassmembernames()
            optimizationpasses(4)
            overloadaggressively()
            mergeinterfacesaggressively()
        }
        withType(net.fabricmc.loom.task.RemapJarTask::class) {
            dependsOn(shrinkJar)
            val shrinkedJar = shrinkJar.get().outJarFileCollection.singleFile
            archiveBaseName.set(shadowJar.archiveBaseName)
            archiveVersion.set("[${rootProject.version}+$mcBase]")
            archiveClassifier.set(this@subprojects.name)
            input.set(shrinkedJar)
            if (!archiveFileName.get().contains("common")) destinationDirectory.set(file("$rootDir/remappedJars"))
        }
        getByName("modrinth").dependsOn(build)
    }
}

allprojects {
    /** configurations.all {
        resolutionStrategy {
            eachDependency {
                if (requested.group == "org.jetbrains.kotlin" && requested.name.contains("stdlib") && (requested.version != kotlinVer)) {
                    useVersion(kotlinVer)
                }
            }
        }
    }
    **/
    apply(plugin = "java")
    apply(plugin = "org.jetbrains.kotlin.jvm")
    apply(plugin = "architectury-plugin")
    dependencies {
        implementation(kotlin("stdlib-jdk8", kotlinVer))
        implementation(kotlin("reflect", kotlinVer))
        annotationProcessor("com.github.bsideup.jabel:jabel-javac-plugin:0.4.2")
    }
    tasks {
        withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile::class) {
            with(kotlinOptions) {
                // https://github.com/JetBrains/kotlin/blob/master/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt
                freeCompilerArgs = listOf(
                    "-Xjvm-default=all", "-Xlambdas=indy", "-Xtype-enhancement-improvements-strict-mode",
                    "-Xmultifile-parts-inherit",
                    "-Xbackend-threads=0", "-Xno-param-assertions", "-Xno-call-assertions",
                    "-opt-in=kotlin.RequiresOptIn", "-Xextended-compiler-checks", "-Xassertions=jvm", "-progressive"
                )
                jvmTarget = if (javaVer.toInt() < 9) "1.$javaVer" else javaVer
                languageVersion = (kotlinSplitVersion[0] + '.' + (kotlinSplitVersion[1].toShort() + 1).toString())
                apiVersion = "${kotlinSplitVersion[0]}.${kotlinSplitVersion[1]}"
            }
        }
        withType(JavaCompile::class) {
            with(options) {
                encoding = "UTF-8"
                isFork = true
                release.set(javaVer.toInt())
                sourceCompatibility = "11"
                targetCompatibility = javaVer
                compilerArgs.add("-Xplugin:jabel")
            }
        }
        withType(ProcessResources::class) {
            with(project(":common").sourceSets.main.get().resources.srcDirs) {
                if (!sourceSets.main.get().resources.srcDirs.containsAll(this)) {
                    from(this)
                }
            }
            val modProperties = mapOf(
                "mod_version" to (rootProject.version as String),
                "minecraft_version" to mcBase,
                "java_version" to javaVer,
                "mod_group" to this@allprojects.group,
                "fabric_kotlin" to rootProject.property("fabric_language_kotlin"),
                "kotlinforforge" to rootProject.property("kotlinforforge"),
                "clothconfig" to rootProject.property("clothconfig_version")
            )
            inputs.properties(modProperties)
            filesNotMatching(listOf("*.png")) {
                expand(modProperties)
            }
        }
        check {
            setDependsOn(
                dependsOn.minus(test)
            )
        }
    }
}
repositories {
    mavenCentral()
}

file("remappedJars").let { if (it.exists()) tasks.clean.get().delete.add(it) }