/*
 * Copyright (C) 2021 Emerson Pinter - All Rights Reserved
 */

buildscript {
    repositories {
        mavenCentral()
    }
}

plugins {
    id "application"
    id "java"
    id "org.sonarqube" version "5.0.0.4638"
}

group = 'br.com.pinter.tqrespec'

apply from: "gradle/patchModulesJar.gradle"

import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform

sourceSets {
    main.output.resourcesDir = main.java.classesDirectory.get()
}

OperatingSystem OS = DefaultNativePlatform.currentOperatingSystem
ext.distAppName = 'TQRespec'
version = '1.1.3'

application {
    mainClass = "br.com.pinter.tqrespec.Main"
}

java {
    sourceCompatibility = JavaVersion.VERSION_21
}

project.ext {
    moduleName = 'br.com.pinter.tqrespec'
    mainPackageName = 'br.com.pinter.tqrespec'
    javahome = System.getProperty("java.home")
    moduleInfoPresent = false
    osArch = System.getProperty("os.arch").toLowerCase()
    javafxSdkDir = file(String.format("%s/sdk/javafx-sdk-%s-%s-%s",
            projectDir, compileJava.javaCompiler.get().metadata.languageVersion,
            OS.toFamilyName().toLowerCase(), osArch))
    javafxSdkBinDir = file("${javafxSdkDir}/bin")
    javafxSdkLibDir = file("${javafxSdkDir}/lib")
    appArchiveOs = ""
    appArchiveArch = ""
    // toggle patch-jar feature, enable jpms on non-modular jars
    patchJars = true
    // directory for patched jars
    patchjarDir = layout.buildDirectory.dir('patchedjar').get().asFile.getPath()
    // modules dependencies to be passed to jdeps
    patchJarModuleDeps = [
            "com.google.guice" : ["com.google.common", "aopalliance", "jakarta.inject"],
            "com.google.common": ["com.google.errorprone.annotations", "j2objc.annotations", "jsr305", "failureaccess"],
    ]
    //ignore dependencies when generating module-info for the given package
    //use only when all required deps are satisfied
    patchJarIgnoreMissingDeps = ["com.google.guice"]
}

if (file('src/main/java/module-info.java').exists()) {
    ext['moduleInfoPresent'] = true
}

if (!file(javafxSdkDir).directory) {
    throw new GradleException(String.format("ERROR: openjfx-sdk for java '%s' (%s) not found in %s", compileJava.javaCompiler.get().metadata.languageVersion, System.getProperty("os.arch"), javafxSdkDir))
}

if (project.hasProperty('buildVersion')) {
    project.version = project.properties.get("buildVersion")
}

if (!OS.isWindows()) {
    ext['appArchiveOs'] = String.format("-%s", OS.toFamilyName().toLowerCase())
}
if (osArch != "amd64") {
    ext['appArchiveArch'] = String.format("-%s", ext.osArch)
}

ext['buildPackageName'] = "${distAppName}-${project.version}${ext.appArchiveOs}${ext.appArchiveArch}"
ext['appArchiveName'] = "${ext.buildPackageName}.zip"

repositories {
    mavenCentral()
}

configurations {
    mockitoAgent
}

dependencies {
    implementation fileTree(dir: javafxSdkLibDir, include: [
            'javafx.base.jar', 'javafx.controls.jar', 'javafx.fxml.jar', 'javafx.graphics.jar'])
    implementation libs.guava
    implementation libs.guice
    implementation libs.commons.lang3
    implementation libs.commons.text
    implementation libs.jna
    implementation libs.jackson.core
    implementation libs.jackson.annotations
    implementation libs.jackson.databind
    implementation libs.picocli

    implementation project(':tqdatabase')

    testImplementation libs.junit
    testImplementation libs.junit.engine
    testRuntimeOnly libs.junit.launcher
    testImplementation libs.mockito.core
    testImplementation libs.mockito.junit.jupiter
    mockitoAgent(libs.mockito.core) {
        transitive = false
    }
    testImplementation libs.guava
    testImplementation libs.guice
}

test {
    useJUnitPlatform()
    jvmArgs '--add-opens', 'java.base/java.lang=ALL-UNNAMED',
            '--add-opens', 'java.base/java.util.concurrent=ALL-UNNAMED',
            '--add-opens', 'java.base/java.io=ALL-UNNAMED',
            '--add-opens', 'java.base/java.nio=ALL-UNNAMED',
            '--add-opens', 'java.base/java.nio.file=ALL-UNNAMED',
            '--add-opens', 'java.logging/java.util.logging=ALL-UNNAMED',
            "-javaagent:${configurations.mockitoAgent.asPath}"
}

if (OS.isWindows()) {
    ext.binjlink = javahome + "/bin/jlink.exe"
    ext.binjpackage = javahome + "/bin/jpackage.exe"
} else {
    ext.binjlink = javahome + "/bin/jlink"
    ext.binjpackage = javahome + "/bin/jpackage"
}

jar {
    //noinspection GroovyAssignabilityCheck
    manifest {
        attributes('Class-Path': configurations.runtimeClasspath.collect { it.getName() }.join(' '),
                'Implementation-Title': distAppName,
                'Implementation-Version': project.version,
                'Implementation-Vendor': 'Emerson Pinter',
                'Automatic-Module-Name': moduleName,
                'Main-Class': application.mainClass.get())
    }
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}

tasks.register('copyMetaInf', Copy) {
    group = 'tqrespec'
    from fileTree(new File(sourceSets.main.output.resourcesDir, '/META-INF'))
    into sourceSets.main.java.classesDirectory.get().dir('/META-INF')
}

if (moduleInfoPresent) {
    compileJava {
        inputs.property("moduleName", moduleName)
        doFirst {
            options.compilerArgs = [
                    '--module-path', classpath.asPath,
            ]
            classpath = files()
        }
    }

    run {
        inputs.property("moduleName", moduleName)
        doFirst {
            jvmArgs = [
                    '-XX:+UseG1GC',
                    '-Dguice_bytecode_gen_option=DISABLED',
                    '-XX:MinHeapFreeRatio=2', '-XX:MaxHeapFreeRatio=15', '-XX:SoftMaxHeapSize=512M',
                    '-XX:G1PeriodicGCSystemLoadThreshold=0', '-XX:G1PeriodicGCInterval=7000', '-XX:-G1PeriodicGCInvokesConcurrent',
                    '--module-path', classpath.asPath,
                    '--class-path', classpath.asPath,
                    '--module', moduleName,
            ] as List<String>
            classpath = files()
        }
    }
} else {
    run {
        doFirst {
            jvmArgs = [
                    '-XX:+UseG1GC',
                    '-XX:MinHeapFreeRatio=2', '-XX:MaxHeapFreeRatio=15', '-XX:SoftMaxHeapSize=512M',
                    '-XX:G1PeriodicGCSystemLoadThreshold=0', '-XX:G1PeriodicGCInterval=7000', '-XX:-G1PeriodicGCInvokesConcurrent',
                    '--module-path', "${javafxSdkLibDir}",
                    '--add-modules', 'javafx.base,javafx.fxml,javafx.graphics,javafx.controls',
            ]
        }
    }
}

tasks.register('runExtract', JavaExec) {
    dependsOn clean
    dependsOn compileJava
    group = 'tqrespec'

    jvmArgs = [
            '-XX:+UseG1GC',
            '-Dguice_bytecode_gen_option=DISABLED',
            '-XX:MinHeapFreeRatio=2', '-XX:MaxHeapFreeRatio=15', '-XX:SoftMaxHeapSize=1024M',
            '-XX:G1PeriodicGCSystemLoadThreshold=0', '-XX:G1PeriodicGCInterval=10000', '-XX:-G1PeriodicGCInvokesConcurrent',
            '--module-path', run.classpath.asPath
    ] as List<String>
    mainModule = "${moduleName}/dev.pinter.tqextract.Cli"
}

tasks.register('copyToLib', Copy) {
    group = 'tqrespec'
    doFirst {
        mkdir layout.buildDirectory.dir("jardist")
    }

    from(configurations.runtimeClasspath.findAll { !it.getName().toLowerCase().matches("javafx.*\\.jar") }).collect { it.getPath() }
    from jar
    into layout.buildDirectory.dir("jardist")
}

copyToLib.dependsOn(jar)
classes.dependsOn('copyMetaInf')

tasks.register('copyJarsToModuleDeps', Copy) {
    group = 'tqrespec'
    if (patchJars) {
        from project(':tqdatabase').configurations.compileClasspath
        from project.configurations.compileClasspath
        into patchjarDir
    }
}

tasks.register('patchModulesJar', PatchJarModuleTask) {
    group = 'tqrespec'
    dependsOn 'copyJarsToModuleDeps'
    dependsOn 'copyToLib'
    delete = true
    ignoreMissingDeps = patchJarIgnoreMissingDeps
    tempPatchDir = layout.buildDirectory.dir('temppatch').get().asFile.getPath()
    tempJarDir = patchjarDir
    moduleDeps = patchJarModuleDeps
    modulesDepsDir = layout.buildDirectory.dir("jardist").get().asFile
}

tasks.register('createDirs') {
    doLast {
        mkdir layout.buildDirectory.dir("appimage-files")
        mkdir layout.buildDirectory.dir("appimage-modules")
    }
}

tasks.register('copyAppimageModules', Copy) {
    duplicatesStrategy = DuplicatesStrategy.WARN
    group = 'tqrespec'
    dependsOn jar
    dependsOn 'tqdatabase:jar'
    if (patchJars) {
        dependsOn patchModulesJar
    }

    from tasks.jar.outputs.files.collect { it.getPath() }
    from configurations.runtimeClasspath.filter {
        !file(patchjarDir).listFiles().collect { p -> p.getName().toLowerCase() }.contains(it.getName().toLowerCase())
    }
    from project(':tqdatabase').tasks.jar.outputs.files.collect { it.getPath() }

    if (moduleInfoPresent && patchJars) {
        from patchjarDir
    }

    into layout.buildDirectory.dir("appimage-modules")
}

tasks.register('copyAppimageFiles') {
    group = 'tqrespec'
    dependsOn jar
    dependsOn 'tqdatabase:jar'
    dependsOn copyAppimageModules

    def excludeJars = new ArrayList<>()

    doFirst {
        excludeJars.addAll(configurations.runtimeClasspath.collect { x -> x.getName() })
    }

    doLast {
        copy {
            if (!moduleInfoPresent) {
                from project(':tqdatabase').tasks.jar.outputs.files.collect { it.getPath() }
                from jar
            }

            from configurations.runtimeClasspath.findAll {
                !excludeJars.contains(it.getName().toLowerCase())
            }.collect()

            into layout.buildDirectory.dir("appimage-files")
        }
    }
}

tasks.register('jlink', Exec) {
    group = 'tqrespec'
    dependsOn createDirs
    dependsOn 'copyAppimageFiles'
    workingDir layout.buildDirectory.get().asFile

    doFirst {
        if (moduleInfoPresent) {
            commandLine binjlink,
                    '-J-Djlink.debug=true',
                    '--module-path', layout.buildDirectory.dir("appimage-modules").get().asFile,
                    '--add-modules', "ALL-MODULE-PATH",
                    '--add-modules', 'jdk.crypto.cryptoki',
                    '--output', layout.buildDirectory.dir("jre-image").get().asFile,
                    '--compress', 'zip-6',
                    '--no-header-files',
                    '--no-man-pages',
                    '--strip-native-commands'
        } else {
            commandLine binjlink,
                    '--module-path', layout.buildDirectory.dir("appimage-modules").get().asFile,
                    '--add-modules', "ALL-MODULE-PATH",
                    '--output', layout.buildDirectory.dir("jre-image").get().asFile,
                    '--compress', 'zip-6',
                    '--no-header-files',
                    '--no-man-pages',
                    '--strip-native-commands'
        }
    }
}

tasks.register('archiveAppimage', Zip) {
    group = 'tqrespec'
    dependsOn clean
    dependsOn 'appimage'
    doLast {
        println("Creating archive '${appArchiveName}'")
    }
    from layout.buildDirectory.dir("app-image").get().asFile
    archiveFileName = appArchiveName
    destinationDirectory = layout.buildDirectory.dir("appimage-dist").get().asFile
}

tasks.register('appimage') {
    group = 'tqrespec'
    dependsOn clean
    dependsOn jlink

    doFirst {
        def appIcon = "${projectDir}/src/main/resources/icon/icon256.ico"
        if (!OS.isWindows()) {
            appIcon = "${projectDir}/src/main/resources/icon/icon256.png"
        }

        def tqextractLauncher = File.createTempFile("tqextractlauncher", ".properties", file(layout.buildDirectory))
        tqextractLauncher.write("module=${moduleName}/dev.pinter.tqextract.Cli" + System.lineSeparator())
        tqextractLauncher.append("java-options=-XX:+UseG1GC -XX:G1PeriodicGCInterval=10000 -XX:SoftMaxHeapSize=1024M" +
                " -XX:MinHeapFreeRatio=2 -XX:MaxHeapFreeRatio=15 -XX:G1PeriodicGCSystemLoadThreshold=0" +
                " -Dguice_bytecode_gen_option=DISABLED" + System.lineSeparator())

        def texconverterLauncher = File.createTempFile("texconverterlauncher", ".properties", file(layout.buildDirectory))
        texconverterLauncher.write("module=${moduleName}/dev.pinter.tqextract.TexConverterCli" + System.lineSeparator())
        texconverterLauncher.append("java-options=-XX:+UseG1GC -XX:G1PeriodicGCInterval=10000 -XX:SoftMaxHeapSize=1024M" +
                " -XX:MinHeapFreeRatio=2 -XX:MaxHeapFreeRatio=15 -XX:G1PeriodicGCSystemLoadThreshold=0" +
                " -Dguice_bytecode_gen_option=DISABLED" + System.lineSeparator())

        if (OS.isWindows()) {
            tqextractLauncher.append("win-console=true" + System.lineSeparator())
            texconverterLauncher.append("win-console=true" + System.lineSeparator())
        }
        tqextractLauncher.deleteOnExit()

        def jpackage = providers.exec {
            ignoreExitValue true
            workingDir file(layout.buildDirectory).absolutePath
            if (moduleInfoPresent) {
                commandLine binjpackage,
                        '--type', 'app-image',
                        '--verbose',
                        '--runtime-image', layout.buildDirectory.dir("jre-image").get().asFile,
                        '--module', "${moduleName}/${application.mainClass.get()}",
                        '--dest', layout.buildDirectory.dir("app-image").get().asFile,
                        '--name', distAppName,
                        '--icon', "${appIcon}",
                        '--app-version', version,
                        '--vendor', 'Emerson Pinter',
                        '--input', layout.buildDirectory.dir("appimage-files").get().asFile,
                        '--description', 'The respec tool for Titan Quest game',
                        '--copyright', 'Copyright (C) 2025 Emerson Pinter',
                        '--add-launcher', "tqextract-cli=${tqextractLauncher.absolutePath}",
                        '--add-launcher', "texconverter-cli=${texconverterLauncher.absolutePath}",
                        '--java-options',
                        '-XX:+UseG1GC ' +
                                '-Dguice_bytecode_gen_option=DISABLED ' +
                                '-XX:MinHeapFreeRatio=2 -XX:MaxHeapFreeRatio=15 -XX:SoftMaxHeapSize=512M ' +
                                '-XX:G1PeriodicGCSystemLoadThreshold=0 -XX:G1PeriodicGCInterval=7000 -XX:-G1PeriodicGCInvokesConcurrent'
            } else {
                commandLine binjpackage,
                        '--type', 'app-image',
                        '--verbose',
                        '--runtime-image', layout.buildDirectory.dir("jre-image").get().asFile,
                        '--dest', layout.buildDirectory.dir("app-image").get().asFile,
                        '--name', distAppName,
                        '--icon', "${appIcon}",
                        '--app-version', version,
                        '--vendor', 'Emerson Pinter',
                        '--input', layout.buildDirectory.dir("appimage-files").get().asFile,
                        '--description', 'The respec tool for Titan Quest game',
                        '--copyright', 'Copyright (C) 2025 Emerson Pinter',
                        '--main-jar', jar.outputs.files.collect { it.getName() }.get(0),
                        '--main-class', $ { application.mainClass.get() },
                        '--java-options',
                        '-XX:+UseG1GC ' +
                                '-Dguice_bytecode_gen_option=DISABLED ' +
                                '-XX:MinHeapFreeRatio=2 -XX:MaxHeapFreeRatio=15 -XX:SoftMaxHeapSize=512M ' +
                                '-XX:G1PeriodicGCSystemLoadThreshold=0 -XX:G1PeriodicGCInterval=7000 -XX:-G1PeriodicGCInvokesConcurrent' +
                                '--add-opens javafx.base/com.sun.javafx.reflect=ALL-UNNAMED ' +
                                '--add-opens java.base/java.lang=ALL-UNNAMED'
            }
        }

        if (jpackage.result.get().exitValue == 0) {
            System.out.println jpackage.standardOutput.asText.get()
            System.out.println jpackage.standardError.asText.get()
        } else {
            System.err.println jpackage.standardOutput.asText.get()
            System.err.println jpackage.standardError.asText.get()
        }
    }

    doLast {
        copy {
            if (OS.isWindows()) {
                from javafxSdkBinDir.listFiles().findAll {
                    it.getName().equalsIgnoreCase("decora_sse.dll") ||
                            it.getName().equalsIgnoreCase("fxplugins.dll") ||
                            it.getName().equalsIgnoreCase("glass.dll") ||
                            it.getName().equalsIgnoreCase("javafx_font.dll") ||
                            it.getName().equalsIgnoreCase("javafx_iio.dll") ||
                            it.getName().equalsIgnoreCase("glib-lite.dll") ||
                            it.getName().equalsIgnoreCase("prism_common.dll") ||
                            it.getName().equalsIgnoreCase("prism_d3d.dll") ||
                            it.getName().equalsIgnoreCase("prism_sw.dll")
                }.collect()

                into layout.buildDirectory.dir("app-image/${distAppName}")
            } else {
                from javafxSdkLibDir.listFiles().findAll { it.getName().toLowerCase().endsWith('.so') }.collect()
                into layout.buildDirectory.dir("app-image/${distAppName}/lib/runtime/lib")
            }
        }

        if (OS.isWindows()) {
            copy {
                from javafxSdkBinDir.listFiles().findAll {
                    it.getName().equalsIgnoreCase("ucrtbase.dll") ||
                            it.getName().toLowerCase().startsWith("vcruntime") ||
                            it.getName().toLowerCase().startsWith("msvcp") ||
                            it.getName().toLowerCase().startsWith("api-ms-win-core") ||
                            it.getName().toLowerCase().startsWith("api-ms-win-crt")
                }.collect()

                into layout.buildDirectory.dir("app-image/${distAppName}/runtime/bin")
            }
        }

        copy {
            from "${projectDir}/README.md"
            into layout.buildDirectory.dir("app-image/${distAppName}")
        }
        if (OS.isLinux()) {
            copy {
                from "${projectDir}/src/dist/tqextract-cli.sh"
                from "${projectDir}/src/dist/texconverter-cli.sh"
                from "${projectDir}/src/dist/TQRespec.sh"
                into layout.buildDirectory.dir("app-image/${distAppName}")
            }
        }
        layout.buildDirectory.dir("app-image/${distAppName}/savedata").get().asFile.mkdir()
        layout.buildDirectory.dir("app-image/${distAppName}/gamedata").get().asFile.mkdir()
        layout.buildDirectory.dir("jre-image").get().asFile.deleteDir()
    }
}

tasks.register('prepBuildEnv') {
    doFirst {
        file(layout.buildDirectory.get().file("build-packagename.txt")).write("${buildPackageName}")
        if (System.getenv("GITHUB_ENV") != null) {
            println "GitHub Actions found"
            file(System.getenv("GITHUB_ENV")).withWriterAppend { out ->
                out.println("APP_BUILDPACKAGENAME=${buildPackageName}")
                out.println("APP_NAME=${distAppName}")
                out.println("APP_VERSION=${project.version}")
            }
        }
    }
}
appimage.dependsOn 'prepBuildEnv'

clean.dependsOn subprojects.collect { it.tasks.matching { it.name == 'clean' } }