/*
 * Copyright (c) 2023 Christian Kierdorf
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the “Software”),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

plugins {
    id 'application' // Java application plugin
    id 'org.openjfx.javafxplugin' version '0.1.0'
}

javafx {
    // Configures the JavaFX plugin with the required version and modules
    version = '23.0.2'
    modules = ['javafx.controls', // Enables JavaFX controls module
               'javafx.fxml', // Enables JavaFX FXML module
               'javafx.graphics' // Enables JavaFX graphics module
    ]
}

dependencies {
    // Adds dependency on the project named ':base'
    implementation project(':base')
    implementation 'ch.qos.logback:logback-classic:1.5.16'
    // Add structured logging support
    implementation 'net.logstash.logback:logstash-logback-encoder:8.1'

    // Provide org.json at runtime for desktop, since base uses compileOnly
    runtimeOnly 'org.json:json:20250517'

    // TestFX dependencies for UI testing
    testImplementation 'org.testfx:testfx-core:4.0.18'
    testImplementation 'org.testfx:testfx-junit5:4.0.18'
    testImplementation 'org.hamcrest:hamcrest:3.0'
}

// Application-specific configurations
application {
    // Specifies the main class of the application, which contains the `main()` method
    mainClass = 'de.schliweb.bluesharpbendingapp.app.Launcher'
    // Sets the name of the application
    applicationName = 'BluesHarpBendingApp'
}

jar {
    manifest {
        // Adds version information to the `MANIFEST.MF` file, which can be accessed programmatically (e.g., using getImplementationVersion())
        attributes('Implementation-Version': version, // Defines the implementation version
                'Main-Class': application.mainClass // Sets the main class for the application
        )
    }
    // Configures behavior to exclude duplicate files
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    // Adds all files from the runtime classpath to the JAR file, unpacking non-directory files first
    // This is especially necessary to integrate JavaFX into the JAR
    from {
        configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
    }
}

import org.gradle.internal.os.OperatingSystem

// Declares Java modules required for the application
def jvmModules = ['java.base','java.naming', 'java.desktop', 'java.scripting', 'jdk.unsupported']
def jdkPath = System.properties['java.home'] // Gets the JDK base path
def jdkModulesPath = "${jdkPath}/jmods" // Defines the path to JDK modules

tasks.register('createRuntime', Exec) {

    // Ensures the 'clean' task is executed before this task
    dependsOn 'clean'

    group = 'build' // Sets the task group for organizational purposes
    description = 'Creates a custom JRE using jlink'

    def outputDir = layout.buildDirectory.dir("runtime").get().asFile
    // Specifies the output directory for the runtime image

    commandLine = ["${jdkPath}/bin/jlink", // Calls the jlink tool
                   '--module-path', "$jdkModulesPath", // Specifies the module path for the tool
                   '--add-modules', jvmModules.join(','), // Adds the required modules
                   '--output', outputDir, // Defines the output directory for the runtime image
                   '--strip-debug', // Removes debug information
                   '--compress', '2', // Compresses resources in the runtime image
                   '--no-header-files', // Excludes header files from the runtime image
                   '--no-man-pages' // Excludes man pages from the runtime image
    ] as List<String>

    doFirst {
        println "Creating custom runtime with the following parameters:"
        println "Module Path: ${jdkModulesPath}"
        println "Modules: ${jvmModules.join(',')}"
        println "Output Directory: ${outputDir}"
    }
}

tasks.register('jpackage', Exec) {
    group = 'build' // Sets the task group for organizational purposes
    description = 'Creates a native installer file using jpackage'

    // Ensures dependencies are created before executing this task
    dependsOn 'createRuntime'
    dependsOn 'jar'

    def currentOs = OperatingSystem.current() // Detects the operating system
    def runtimeDir = layout.buildDirectory.dir("runtime").get().asFile // Specifies the directory for the runtime image
    def jarDir = layout.buildDirectory.dir("libs").get().asFile // Specifies the directory for the main JAR file
    def outputDir = layout.buildDirectory.dir("jpackage").get().asFile
    // Specifies the output directory for the installer

    // Determines the icon file type based on the operating system
    def imgType = currentOs.windows ? 'ico' : currentOs.macOsX ? 'icns' : 'png'
    def imageIcon = file("src/main/resources/ic_launcher_round.${imgType}").absolutePath
    // Sets the path to the application icon

    def currentArch = System.getProperty("os.arch")
    def archSuffix = ""
    if (currentOs.macOsX) {
        if (currentArch.contains("x86_64")) {
            archSuffix = "-x64"
        } else if (currentArch.contains("aarch64")) {
            archSuffix = "-arm64"
        } else {
            archSuffix = "-unknownArch"
        }
    }

    // Retrieves project version and application information
    def appVersion = project.version
    def mainJarName = jar.archiveFileName.get()
    def mainJarClass = application.mainClass.get()
    def applicationName = "bluesharpbendingapp${currentOs.macOsX ? archSuffix : ''}"

    def installerOptions = []
    // installerOptions += ['--resource-dir', "src/main/resources"] // Uncomment if additional resources are required

    // Defines metadata about the application's vendor
    installerOptions += ['--vendor', 'letsbend.de']
    // Adds platform-specific installer options
    if (currentOs.windows) {
        // Configures installer options for Windows, including shortcuts and user-level installation
        installerOptions += ['--type', 'msi', '--win-per-user-install', '--win-dir-chooser', '--win-menu', '--win-shortcut']
    } else if (currentOs.linux) {
        // Configures installer options for Linux, including `.deb` packages with shortcuts
        installerOptions += ['--type', 'deb', '--linux-package-name', 'bluesharpbendingapp', '--linux-app-release', version, '--linux-deb-maintainer', 'christian.kierdorf@schliweb.de', '--linux-shortcut']
    } else if (currentOs.macOsX) {
        // Configures installer options for macOS, including the package name and identifier
        installerOptions += ['--type', 'dmg', '--mac-package-name', 'BluesharpBendingApp', '--mac-package-identifier', 'BluesharpBendingApp']
    }

    // Sets the command line for jpackage with its parameters
    commandLine = ["${jdkPath}/bin/jpackage",
                   '--runtime-image', runtimeDir, // Specifies the runtime image directory
                   '--input', jarDir, // Input directory containing the main JAR file
                   '--main-jar', mainJarName, // Main JAR filename
                   '--main-class', mainJarClass, // Main class with the main method
                   '--name', applicationName, // Application name
                   '--icon', imageIcon, // Application icon
                   '--dest', outputDir, // Destination directory for the installer
                   '--app-version', appVersion, // Application version
                   '--arguments', '--logging.profile=PROD' // Set the logging profile argument
    ] + installerOptions as List<String>

    doFirst {
        println "Creating installer with the following values:"
        println "Runtime Directory: ${runtimeDir}"
        println "JAR Directory: ${jarDir}"
        println "Output Directory: ${outputDir}"
    }
}

test {
    // Configures the test task to use JUnit 5 (JUnit Platform)
    useJUnitPlatform()
}
