buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath "com.guardsquare:proguard-gradle:7.3.0"
    }
}

plugins {
    id "java"
}

def osName = System.getProperty("os.name")
def is64 = System.getProperty("os.arch").contains("64")
def isArm = System.getProperty("os.arch").equals("aarch64")
def platform = osName.contains("Linux") ? 'linux' + (is64 ? '64' : '32') :
               osName.contains("Windows") ? 'win' + (is64 ? '64' : '32') :
               osName.contains("OS X") ? 'mac' + (isArm ? "Arm64" : "X86") : 'unknown'

String exec(def line, String dir = ".", boolean failOnError = true) {
    def proc = Runtime.getRuntime().exec(line, null, project.file(dir))
    if (proc.waitFor() == 0) return new String(proc.inputStream.readAllBytes()).trim()
    else {
        if (failOnError) throw new Exception(line.toString() + "\n" + new String(proc.errorStream.readAllBytes()))
        else return null
    }
}

version = exec("git describe --tags", ".", false) ?: "unknown"

sourceCompatibility = targetCompatibility = 17

def javaModules = ["java.base", "java.prefs", "java.logging", "jdk.crypto.ec"]
project.ext.set("javaModules", javaModules)

sourceSets {
    main {
        java { srcDirs "src" }
        resources { srcDirs "config", "src", "resources" }
    }
    test {
        java { srcDir "test" }
        resources { srcDir "test" }
    }
}

repositories {
    mavenCentral()
}

configurations {
    jna
    any
    linux64
    win64.extendsFrom(jna)
    macX86
    macArm64

    // compile and test with current platform's SWT and JNA
    compileOnly.extendsFrom(getAt(platform), jna)
    testImplementation.extendsFrom(compileOnly)
    runtimeClasspath.extendsFrom(compileOnly)
    all*.exclude module: 'org.eclipse.swt' // transitive dependency in Maven that fails
}

dependencies {
    def swtVersion = '3.129.0'
    linux64 "org.eclipse.platform:org.eclipse.swt.gtk.linux.x86_64:${swtVersion}"
    win64 "org.eclipse.platform:org.eclipse.swt.win32.win32.x86_64:${swtVersion}"
    macX86 "org.eclipse.platform:org.eclipse.swt.cocoa.macosx.x86_64:${swtVersion}"
    macArm64 "org.eclipse.platform:org.eclipse.swt.cocoa.macosx.aarch64:${swtVersion}"
    jna 'net.java.dev.jna:jna:5.9.0'
    testImplementation 'junit:junit:4.13.2'
    testImplementation 'org.mockito:mockito-core:2.23.0'
}

test {
    if (platform.startsWith("mac")) {
        // jvmArgs "-XstartOnFirstThread"
        println("WARNING: tests that touch GUI will fail on Mac due to Cocoa restrictions and Gradle unable to run tests on main thread")
        filter {
            includeTestsMatching "net.azib.ipscan.core.net.*"
        }
    }
}

def packageTask(String platform, Closure doMore) {
    return tasks.create(platform, Jar) {
        dependsOn = ['classes']
        manifest {
           attributes 'Implementation-Title': 'Angry IP Scanner',
               'Implementation-Version': version,
               'Main-Class': 'net.azib.ipscan.Main',
               'Class-Path': platform == "any" ? "/usr/share/java/swt4.jar /usr/lib/eclipse/swt.jar ./" : "./",
               'Title': 'Angry IP Scanner',
               'Version': version,
               'Build-Date': java.time.LocalDate.now().toString(),
               'URL': 'https://angryip.org/'
        }
        archiveBaseName.set(project.name + '-' + platform)
        from {
           configurations[platform].collect {
               it.isDirectory() ? it : zipTree(it)
           }
        }
        exclude(
           'images/**/*.svg',
           'META-INF/ECLIPSE*',
           'about_files/*',
           'version.txt',
           '.api_description',
           'about.html',
           'fragment.properties',
           'chrome.manifest',
           // Linux/GTK
           'libswt-awt-gtk-*.so',
           'libswt-glx-gtk-*.so',
           'libswt-webkit-gtk-*.so',
           'webkitextensions*/*',
           // Win32
           'swt-awt-*.dll',
           'swt-wgl-*.dll',
           'swt-gdip-*.dll',
           'swt-webkit-*.dll',
           // Mac
           'libswt-awt-*.jnilib',
            // JNA
            'com/sun/jna/*/*.a',
            'com/sun/jna/*/*.so',
            'com/sun/jna/*/*.jnilib',
            "com/sun/jna/win32-${platform == 'win32' ? 'x86-64' : platform == 'win64' ? 'x86' : '*'}/*.dll",
            // Win-specific stuff
            !platform.startsWith('win') ? 'net/azib/ipscan/core/net/Win*' : '',
            !platform.startsWith('win') ? 'net/azib/ipscan/fetchers/Win*' : ''
        )
        with jar

        if (platform != "any")
            finalizedBy minimizeTask(platform, doMore)
        else
            doLast(doMore)
    }
}

def minimizeTask(String platform, Closure doMore) {
    return tasks.create("${platform}.min", proguard.gradle.ProGuardTask) {
        injars "build/libs/${project.name}-${platform}-${version}.jar"
        outjars "build/libs/${project.name}-${platform}-${version}.min.jar"
        (project.ext.javaModules + "java.desktop").each {
            libraryjars System.getProperty('java.home') + "/jmods/${it}.jmod"
        }
        dontobfuscate
        dontoptimize
        dontnote '**'
        configuration 'ext/swt.pro'
        configuration 'ext/jna.pro'

        def keepClasses = [
            'net.azib.ipscan.**',
        ]

        for (keepClass in keepClasses) {
            keep access: 'public', name: keepClass, {
                method access: 'public'
            }
        }

        doLast {
            ant.move(file: "build/libs/${project.name}-${platform}-${version}.min.jar", tofile: "build/libs/${project.name}-${platform}-${version}.jar")
            doMore()
        }
    }
}

def winLauncher(def platform) {
    ant.concat(destfile: "build/libs/${project.name}-${platform}-${version}.exe", binary: true) {
        ant.fileset(file: "ext/win-launcher/launcher.exe")
        ant.fileset(file: "build/libs/${project.name}-${platform}-${version}.jar")
    }
    ant.delete(file: "build/libs/${project.name}-${platform}-${version}.jar")
}

def deb(def platform, def arch, def moreDeps = '') {
    def dist = buildDir.path + "/libs/deb"
    ant.mkdir(dir: dist)
    ant.copy(todir: dist) {
        ant.fileset(dir: "ext/deb-bundle")
    }
    ant.copy(file: "build/libs/${project.name}-${platform}-${version}.jar", todir: "${dist}/usr/lib/ipscan")
    ant.copy(file: "resources/images/icon128.png", tofile: "${dist}/usr/share/pixmaps/ipscan.png")

    ant.replace(file: "${dist}/DEBIAN/control") {
        ant.replacefilter(token: "VERSION", value: version)
        ant.replacefilter(token: "ARCH", value: arch)
        ant.replacefilter(token: "DEPENDS ", value: moreDeps)
    }

    exec("chmod +x usr/bin/ipscan usr/lib/ipscan/${project.name}-${platform}-${version}.jar", dist)
    exec("fakeroot dpkg-deb -Zxz -b deb ${project.name}_${version}_${arch}.deb", "$dist/..")
    ant.delete(dir: dist)
}

def rpm(def platform, def arch) {
    def dist = buildDir.path + '/libs/rpm'
    def rpmVersion = version.replace('-', '.')
    ant.mkdir(dir: dist)
    ant.copy(todir: dist) {
        ant.fileset(dir: "ext/rpmbuild")
    }
    ant.replace(file: "${dist}/SPECS/ipscan.spec") {
        ant.replacefilter(token: "RPM_VERSION", value: rpmVersion)
        ant.replacefilter(token: "VERSION", value: version)
    }
    exec(new String[] {"sh", "-c", "rpmbuild --target ${arch} --define \"_topdir ${new File(dist).absolutePath}\" --define \"platform ${platform}\" -bb SPECS/ipscan.spec"}, dist)
    ant.move(file: "${dist}/RPMS/${arch}/ipscan-${rpmVersion}-1.${arch}.rpm", todir:'build/libs')
    ant.delete(dir: dist)
}

def macLauncher(def platform) {
    def dist = buildDir.path + '/libs'
    def name = 'Angry IP Scanner'
    exec("cp -rp ext/mac-bundle/. $dist")
    ant.copy(todir: dist) {
        ant.fileset(dir: "ext/mac-bundle")
    }

    def target = "${dist}/${name}.app/Contents/MacOS"
    ant.move(file: "${dist}/${project.name}-${platform}-${version}.jar", todir: target)
    jlink(target, "jre")

    ant.replace(file: "${dist}/${name}.app/Contents/Info.plist") {
        ant.replacefilter(token: "APPNAME", value: name)
        ant.replacefilter(token: "VERSION_NUM", value: version.replaceFirst('-.*', ''))
        ant.replacefilter(token: "VERSION", value: version)
    }

    ant.delete(file: "${dist}/${name}.app/Contents/MacOS/jre/bin/keytool")

    def zipName = "${dist}/${project.name}-${platform}-${version}.zip"
    exec("zip -R $zipName * -x*.zip", dist)

    if (System.getenv("APPLE_USER")) {
        // TODO: codesign --sign Example --options runtime --entitlements test/test.entitlements --force build/Release/test.app
        // TODO: https://developer.apple.com/documentation/xcode/notarizing_macos_software_before_distribution/customizing_the_notarization_workflow
        println("Sending zip file to Apple for notarization")
        exec("xcrun altool --list-providers --username ${System.getenv("APPLE_USER")} --password ${System.getenv("APPLE_PASSWORD")}", ".", false)
        exec("xcrun altool --notarize-app --primary-bundle-id net.azib.ipscan.zip --username ${System.getenv("APPLE_USER")} --password ${System.getenv("APPLE_PASSWORD")} --file ${zipName}", ".", false)
    }

    ant.delete(dir: "${dist}/${name}.app")
}

packageTask('linux64') {
    deb('linux64', 'amd64')
    rpm('linux64', 'x86_64')
}

packageTask('any') {
    deb('any', 'all', 'libswt-gtk-4-java, libswt-cairo-gtk-4-jni,')
}

packageTask('win64') {
    winLauncher('win64')
}

packageTask('macX86') {
    macLauncher("macX86")
}

packageTask('macArm64') {
    macLauncher("macArm64")
}

def jlink(String target, String jrePath, String extraOpts = System.getenv("JLINK_OPTS") ?: "") {
    def jreDir = new File(target, jrePath)
    if (!jreDir.exists()) {
        jreDir.getParentFile().mkdirs()
        exec("jlink --output $jrePath $extraOpts --vm=client --compress=2 --no-header-files --no-man-pages --strip-debug --add-modules " + javaModules.join(","), target)
        ant.delete(dir: new File(jreDir, "legal"))
    }
}

task 'win-installer'(dependsOn: 'win64') {
    doLast {
        def nsisVersion = "3.08"
        def nsis = "nsis-$nsisVersion"
        def installerDir = 'ext/win-installer'
        ant.get(src: "https://sourceforge.net/projects/nsis/files/NSIS%203/${nsisVersion}/${nsis}.zip/download", dest: "$installerDir/${nsis}.zip", skipexisting: 'true')
        ant.unzip(src: "${installerDir}/${nsis}.zip", dest: installerDir)
        ant.replace(file: "${installerDir}/InstallerConfig.nsh") {
            ant.replacefilter(token: "VERSION_MINOR", value: "8")
            ant.replacefilter(token: "VERSION", value: version)
        }
        jlink(installerDir, "AppFiles/jre")
        ant.copy(file: "resources/images/icon.ico", todir: "${installerDir}/AppFiles")
        ant.copy(file: "LICENSE", tofile: "${installerDir}/AppFiles/license.txt")
        ant.copy(file: "build/libs/${project.name}-win64-${version}.exe", tofile:"${installerDir}/AppFiles/ipscan.exe")
        if (platform.startsWith('win')) {
            exec("cmd /c $nsis\\makensis.exe Installer\\Installer.nsi", installerDir)
        }
        else {
            println("Building of Windows Installer is not supported on other platforms")
            exec("wine $nsis/makensis.exe Installer/Installer.nsi", installerDir)
        }
        ant.move(file: "${installerDir}/ipscan-$version-setup.exe", todir: "build/libs")
        exec("git checkout ${installerDir}/InstallerConfig.nsh")
    }
}

task mac(dependsOn: ['macX86', 'macArm64'])

task linux(dependsOn: ['any', 'linux64'])
task current(dependsOn: [platform])

task info {
    doLast {
        println "This script will build ${project.name} ${version}"
        println "Targets:"
        println "  current       - build for current platform ($platform)"
        println "  linux64       - builds only Linux 64-bit binary"
        println "  any           - doesn't bundle SWT, making it possible to run on ARM/Raspbian with libswt provided by OS (Experimental)"
        println "  mac           - builds all Mac binaries"
        println "  macX86        - builds only MacX86 binary"
        println "  macArm64      - builds only MacArm64 binary"
        println "  win64         - builds only Windows 64-bit binary"
        println "  win-installer - packages a Windows installer (with included JRE)"
    }
}

defaultTasks 'info'
