import java.text.SimpleDateFormat

buildscript{
    ext{
        arcHash = property("archash")

        localArc = !project.hasProperty("release") && new File(rootDir.parent, 'Arc').exists() && !project.hasProperty("noLocalArc")

        arcModule = { String name ->
            //skip to last submodule
            name = name.substring(name.lastIndexOf(':') + 1)
            return "com.github.Anuken${localArc ? "" : ".Arc"}:$name:$arcHash"
        }
    }

    repositories{
        mavenLocal()
        mavenCentral()
        google()
        maven{ url "https://oss.sonatype.org/content/repositories/snapshots/" }
        maven{ url 'https://jitpack.io' }
    }

    dependencies{
        classpath arcModule(":extensions:packer")
        classpath arcModule(":arc-core")
    }
}

plugins{
    id "org.jetbrains.kotlin.jvm" version "2.1.10"
    id "org.jetbrains.kotlin.kapt" version "2.1.10"
}

allprojects{
    apply plugin: 'maven-publish'

    version = project.hasProperty("packageVersion") ? project.getProperty("packageVersion") : 'release'
    group = 'com.github.Anuken'

    ext{
        versionNumber = '8'
        if(!project.hasProperty("versionModifier")) versionModifier = 'beta'
        if(!project.hasProperty("versionType")) versionType = 'official'
        appName = 'Mindustry'
        steamworksVersion = '0b86023401880bb5e586bc404bedbaae9b1f1c94'
        rhinoVersion = '73a812444ac388ac2d94013b5cadc8f70b7ea027'

        loadVersionProps = {
            return new Properties().with{p -> p.load(file('../core/assets/version.properties').newReader()); return p }
        }

        debugged = {
            return new File(projectDir.parent, '../Mindustry-Debug').exists() && !project.hasProperty("release") && project.hasProperty("args")
        }

        generateDeployName = { String platform ->
            if(platform == "windows"){
                platform += "64"
            }
            platform = platform.capitalize()

            if(platform.endsWith("64") || platform.endsWith("32")){
                platform = "${platform.substring(0, platform.length() - 2)}-${platform.substring(platform.length() - 2)}bit"
            }

            return "[${platform}]${getModifierString()}[${getNeatVersionString()}]${appName}"
        }

        getVersionString = {
            String buildVersion = getBuildVersion()
            return "$versionNumber-$versionModifier-$buildVersion"
        }

        getNeatVersionString = {
            String buildVersion = getBuildVersion()
            return "v$buildVersion"
        }

        hasSprites = {
            return new File(rootDir, "core/assets/sprites/sprites.aatls").exists()
        }

        getModifierString = {
            if(versionModifier != "release") return "[${versionModifier.toUpperCase()}]"
            return ""
        }

        getBuildVersion = {
            if(!project.hasProperty("buildversion")) return "custom build"
            return project.getProperties()["buildversion"]
        }

        getCommitHash = {
            return 'git rev-parse --verify --short HEAD'.execute().text.trim()
        }

        getPackage = {
            return project.ext.mainClassName.substring(0, project.ext.mainClassName.indexOf("desktop") - 1)
        }

        findSdkDir = {
            //null because IntelliJ doesn't get env variables
            def v = System.getenv("ANDROID_HOME")
            if(v != null) return v
            //rootDir is null here, amazing. brilliant.
            def file = new File(rootDir, "local.properties")
            def props = new Properties().with{p -> p.load(file.newReader()); return p }
            return props.get("sdk.dir")
        }

        generateLocales = {
            def output = 'en\n'
            def bundles = new File(project(':core').projectDir, 'assets/bundles/')
            bundles.list().sort().each{ name ->
                if(name == "bundle.properties") return
                output += name.substring("bundle".length() + 1, name.lastIndexOf('.')) + "\n"
            }
            new File(project(':core').projectDir, 'assets/locales').text = output
            new File(project(':core').projectDir, 'assets/basepartnames').text = new File(project(':core').projectDir, 'assets/baseparts/').list().sort().findAll{f -> f.endsWith(".msch")}.join("\n")
        }

        writeVersion = {
            def pfile = new File(project(':core').projectDir, 'assets/version.properties')
            def props = new Properties()

            try{
                pfile.createNewFile()
            }catch(Exception ignored){
            }

            if(pfile.exists()){
                props.load(new FileInputStream(pfile))

                String buildid = getBuildVersion()
                println("Compiling with build: '$buildid'")

                props["type"] = versionType
                props["number"] = versionNumber
                props["modifier"] = versionModifier
                props["build"] = buildid
                props["buildDate"] = new SimpleDateFormat("MMMM d, yyyy HH:mm a", Locale.getDefault()).format(new Date())
                props["commitHash"] = "unknown"
                if(project.hasProperty("showCommitHash")){
                    props["commitHash"] = getCommitHash()
                }

                props.store(pfile.newWriter(), "Autogenerated file. Do not modify.")
            }
        }

        writeProcessors = {
            new File(rootDir, "annotations/src/main/resources/META-INF/services/").mkdirs()
            def processorFile = new File(rootDir, "annotations/src/main/resources/META-INF/services/javax.annotation.processing.Processor")
            def text = new StringBuilder()
            def files = new File(rootDir, "annotations/src/main/java")
            files.eachFileRecurse(groovy.io.FileType.FILES){ file ->
                if(file.name.endsWith(".java") && (file.text.contains(" extends BaseProcessor") || (file.text.contains(" extends AbstractProcessor") && !file.text.contains("abstract class")))){
                    text.append(file.path.substring(files.path.length() + 1)).append("\n")
                }
            }

            processorFile.text = text.toString().replace(".java", "").replace("/", ".").replace("\\", ".")
        }

        writePlugins = {
            new File(rootDir, "annotations/src/main/resources/META-INF/services/").mkdirs()
            def processorFile = new File(rootDir, "annotations/src/main/resources/META-INF/services/com.sun.source.util.Plugin")
            def text = new StringBuilder()
            def files = new File(rootDir, "annotations/src/main/java")
            files.eachFileRecurse(groovy.io.FileType.FILES){ file ->
                if(file.name.endsWith(".java") && (file.text.contains(" implements Plugin"))){
                    text.append(file.path.substring(files.path.length() + 1)).append("\n")
                }
            }

            processorFile.text = text.toString().replace(".java", "").replace("/", ".").replace("\\", ".")
        }
    }

    repositories{
        mavenLocal()
        mavenCentral()
        maven{ url "https://oss.sonatype.org/content/repositories/snapshots/" }
        maven{ url "https://oss.sonatype.org/content/repositories/releases/" }
        maven{ url 'https://jitpack.io' }
    }

    task clearCache{
        doFirst{
            delete{
                delete "$rootDir/core/assets/cache"
            }
        }
    }

    tasks.withType(JavaCompile){
        targetCompatibility = 8
        sourceCompatibility = JavaVersion.VERSION_17
        options.encoding = "UTF-8"
        options.compilerArgs += ["-Xlint:deprecation"]
        dependsOn clearCache

        options.forkOptions.jvmArgs += [
            '--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED',
            '--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED',
            '--add-opens=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED',
            '--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED',
            '--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED',
            '--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED',
            '--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED',
            '--add-opens=java.base/sun.reflect.annotation=ALL-UNNAMED'
        ]
    }
}

configure(project(":annotations")){
    tasks.withType(JavaCompile){
        targetCompatibility = 8
        sourceCompatibility = 8
        options.fork = true
    }
}

//compile with java 8 compatibility for everything except the annotation project
configure(subprojects - project(":annotations")){
    tasks.withType(JavaCompile){
        options.compilerArgs.addAll(['--release', '8'])
    }

    tasks.withType(Javadoc){
        options{
            addStringOption('Xdoclint:none', '-quiet')
            addStringOption('-release', '17')
            encoding('UTF-8')
        }
    }
}

project(":desktop"){
    apply plugin: "java"

    compileJava.options.fork = true

    dependencies{
        implementation project(":core")
        implementation arcModule("extensions:profiling")
        implementation arcModule("extensions:discord")
        implementation arcModule("natives:natives-filedialogs")
        implementation arcModule("natives:natives-desktop")
        implementation arcModule("natives:natives-freetype-desktop")

        if(debugged()) implementation project(":debug")

        implementation "com.github.Anuken:steamworks4j:$steamworksVersion"

        implementation arcModule("backends:backend-sdl")
        annotationProcessor 'com.github.Anuken:jabel:0.9.0'
    }
}

project(":core"){
    apply plugin: "java-library"
    apply plugin: "kotlin"
    apply plugin: "kotlin-kapt"

    kapt{
        javacOptions{
            option("-source", "17")
            option("-target", "1.8")
        }
    }

    compileJava.options.fork = true

    task preGen{
        outputs.upToDateWhen{ false }
        generateLocales()
        writeVersion()
        writeProcessors()
        writePlugins()
    }

    task copyChangelog{
        doLast{
            def props = loadVersionProps()
            def androidVersion = props['androidBuildCode'].toInteger() - 2
            def loglines = file("../changelog").text.split("\n")
            def notice = "[This is a truncated changelog, see Github for full notes]"
            def maxLength = 460

            def androidLogList = [notice] + loglines.findAll{ line -> !line.endsWith("]") || line.endsWith("[Mobile]") || line.endsWith("[Android]")}
            def result = ""
            androidLogList.forEach{line ->
                if(result.length() + line.length() + 1 < maxLength){
                    result += line + "\n"
                }
            }
            def changelogs = file("../fastlane/metadata/android/en-US/changelogs/")
            changelogs.mkdirs()
            try{
                new File(changelogs, androidVersion + ".txt").text = (result)
            }catch(Exception ignored){
            }
        }
    }

    task sourcesJar(type: Jar, dependsOn: classes){
        archiveClassifier = 'sources'
        from sourceSets.main.allSource
    }

    task assetsJar(type: Jar, dependsOn: ":tools:pack"){
        archiveClassifier = 'assets'
        from files("assets"){
            exclude "config", "cache", "music", "sounds", "sprites/fallback"
        }
    }

    task musicJar(type: Jar){
        archiveClassifier = 'music'
        from files("assets"){
            include "music/*", "sounds/*"
        }
    }

    dependencies{
        compileJava.dependsOn(preGen)

        api "org.lz4:lz4-java:1.8.0"
        api arcModule("arc-core")
        api arcModule("extensions:flabel")
        api arcModule("extensions:freetype")
        api arcModule("extensions:g3d")
        api arcModule("extensions:fx")
        api arcModule("extensions:arcnet")
        implementation arcModule("extensions:filedialogs")
        api "com.github.Anuken:rhino:$rhinoVersion"
        if(localArc && debugged()) api arcModule("extensions:recorder")
        if(localArc) api arcModule(":extensions:packer")

        annotationProcessor 'com.github.Anuken:jabel:0.9.0'
        compileOnly project(":annotations")
        if(!project.hasProperty("noKapt")) kapt project(":annotations")
    }

    afterEvaluate{
        task mergedJavadoc(type: Javadoc){
            def blacklist = [project(":ios"), project(":desktop"), project(":server"), project(":annotations")]
            if(findProject(":android") != null){
                blacklist += project(":android")
            }

            source rootProject.subprojects.collect{ project ->
                if(!blacklist.contains(project) && project.hasProperty("sourceSets")){
                    return project.sourceSets.main.allJava
                }
            }

            classpath = files(rootProject.subprojects.collect { project ->
                if(!blacklist.contains(project) && project.hasProperty("sourceSets")){
                    return project.sourceSets.main.compileClasspath
                }
            })
            destinationDir = new File(buildDir, 'javadoc')
        }
    }

    gradle.taskGraph.whenReady{
        //these are completely unnecessary
        tasks.kaptGenerateStubsKotlin.onlyIf{ false }
        tasks.compileKotlin.onlyIf{ false }
    }

    //comp** classes are only used for code generation
    jar{
        exclude("mindustry/entities/comp/**")
    }
}

project(":server"){
    apply plugin: "java"

    dependencies{
        implementation project(":core")
        implementation arcModule("backends:backend-headless")
        annotationProcessor 'com.github.Anuken:jabel:0.9.0'
    }
}

project(":tests"){
    apply plugin: "java"

    dependencies{
        testImplementation project(":core")
        testImplementation "org.junit.jupiter:junit-jupiter-params:5.7.1"
        testImplementation "org.junit.jupiter:junit-jupiter-api:5.7.1"
        testImplementation arcModule("backends:backend-headless")
        testImplementation "org.json:json:20230618"
        testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.7.1"
    }

    test{
        //fork every test so mods don't interact with each other
        forkEvery = 1
        useJUnitPlatform()
        workingDir = new File("../core/assets")
        testLogging{
            exceptionFormat = 'full'
            showStandardStreams = true
        }
    }
}

project(":tools"){
    apply plugin: "java"

    dependencies{
        implementation project(":core")

        implementation arcModule("natives:natives-desktop")
        implementation arcModule("natives:natives-freetype-desktop")
        implementation arcModule("backends:backend-headless")

        implementation("com.google.guava:guava:33.3.1-jre")
        annotationProcessor 'com.github.Anuken:jabel:0.9.0'
    }
}

project(":annotations"){
    apply plugin: "java-library"

    dependencies{
        implementation 'com.squareup:javapoet:1.12.1'
        implementation arcModule("arc-core")
    }
}

configure([":core", ":server"].collect{project(it)}){
    java{
        withJavadocJar()
        withSourcesJar()
    }

    publishing{
        publications{
            maven(MavenPublication){
                from components.java
                if(project.name == "core"){
                    artifact(tasks.named("assetsJar"))
                }
            }
        }
    }
}

task deployAll{
    task cleanDeployOutput{
        doFirst{
            if(getBuildVersion() == "custom build" || getBuildVersion() == "") throw new IllegalArgumentException("----\n\nSET A BUILD NUMBER FIRST!\n\n----")
            if(!project.hasProperty("release")) throw new IllegalArgumentException("----\n\nSET THE RELEASE PROJECT PROPERTY FIRST!\n\n----")

            delete{
                delete "deploy/"
            }
        }
    }

    dependsOn cleanDeployOutput
    dependsOn "desktop:packrLinux64"
    dependsOn "desktop:packrWindows64"
    dependsOn "desktop:packrWindows32"
    dependsOn "desktop:packrMacOS"
    if(versionModifier != "steam"){
        dependsOn "server:deploy"
        dependsOn "android:deploy"
    }
}

task resolveDependencies{
    doLast{
        rootProject.allprojects{ project ->
            Set<Configuration> configurations = project.buildscript.configurations + project.configurations
            configurations.findAll{c -> c.canBeResolved}.forEach{c -> c.resolve()}
        }
    }
}
