buildscript {
    repositories {
        gradlePluginPortal()
        maven { url 'https://jitpack.io' }
    }
    dependencies {
        classpath 'org.tukaani:xz:1.8'
        classpath 'org.glavo:pack200:0.3.0'
    }
}

plugins {
    id 'application'
    id 'com.github.johnrengelman.shadow' version '7.0.0'
}

import java.nio.file.FileSystems
import java.security.KeyFactory
import java.security.MessageDigest
import java.security.Signature
import java.security.spec.PKCS8EncodedKeySpec
import java.util.jar.JarFile
import java.util.jar.JarOutputStream
import java.util.zip.GZIPOutputStream
import java.util.zip.ZipFile
import java.nio.file.Files

import org.tukaani.xz.LZMA2Options
import org.tukaani.xz.XZOutputStream
import org.glavo.pack200.Pack200

def dev = null
def shortcommit = System.getenv("GITHUB_SHA")?.toLowerCase()?.substring(0, 7) ?: null
if (shortcommit != null && !shortcommit.isEmpty()) dev = "dev-" + shortcommit
def buildnumber = System.getenv("BUILD_NUMBER") ?: dev ?: "SNAPSHOT"
if (System.getenv("BUILD_NUMBER") != null && System.getenv("BUILD_NUMBER_OFFSET") != null)
    buildnumber = (Integer.parseInt(System.getenv("BUILD_NUMBER")) - Integer.parseInt(System.getenv("BUILD_NUMBER_OFFSET"))).toString()
def versionroot = System.getenv("VERSION_ROOT") ?: "3.4"
def microsoftAuthId = System.getenv("MICROSOFT_AUTH_ID") ?: ""
def microsoftAuthSecret = System.getenv("MICROSOFT_AUTH_SECRET") ?: ""
def versionType = System.getenv("VERSION_TYPE") ?: "nightly"
version = versionroot + '.' + buildnumber

mainClassName = 'org.jackhuang.hmcl.Main'

dependencies {
    implementation project(":HMCLCore")
    implementation 'libs:JFoenix'

    implementation group: 'de.javawi.jstun', name: 'jstun', version: '0.7.4'
}

def digest(String algorithm, byte[] bytes) {
    return MessageDigest.getInstance(algorithm).digest(bytes)
}

def createChecksum(File file) {
    def algorithm = "SHA-1"
    def suffix = "sha1"
    new File(file.parentFile, file.name + "." + suffix).text = digest(algorithm, file.bytes).encodeHex().toString() + "\n"
}

def attachSignature(File jar) {
    def keyLocation = System.getenv("HMCL_SIGNATURE_KEY");
    if (keyLocation == null)
        return
    def privatekey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(new File(keyLocation).bytes))

    def signer = Signature.getInstance("SHA512withRSA")
    signer.initSign(privatekey)
    new ZipFile(jar).withCloseable { zip ->
        zip.stream()
                .sorted(Comparator.comparing { it.name })
                .filter { it.name != "META-INF/hmcl_signature" }
                .forEach {
                    signer.update(digest("SHA-512", it.name.getBytes("UTF-8")))
                    signer.update(digest("SHA-512", zip.getInputStream(it).bytes))
                }
    }
    def signature = signer.sign()

    FileSystems.newFileSystem(URI.create("jar:" + jar.toURI()), [:]).withCloseable { zipfs ->
        Files.newOutputStream(zipfs.getPath("META-INF/hmcl_signature")).withCloseable { it << signature }
    }
}

ext.packer = Pack200.newPacker()
packer.properties()["pack.effort"] = "9"
ext.unpacker = Pack200.newUnpacker()

// Pack200 does not guarantee that unpacked .class file is bit-wise same as the .class file before packing
// because of shrinking. So we should pack .class files and unpack it to make sure that after unpacking
// .class files remain the same.
def repack(File file) {
    def packed = new ByteArrayOutputStream()
    new JarFile(file).withCloseable { packer.pack(it, packed) }
    new JarOutputStream(file.newOutputStream()).withCloseable { unpacker.unpack(new ByteArrayInputStream(packed.toByteArray()), it) }
}

sourceSets {
    java11 {
        java {
            srcDirs = ['src/main/java11']
        }
    }
}

compileJava11Java {
    if(JavaVersion.current() < JavaVersion.VERSION_11) {
        javaCompiler = javaToolchains.compilerFor {
            languageVersion = JavaLanguageVersion.of(11)
        }
    }
    options.compilerArgs.add('--add-exports=java.base/jdk.internal.loader=ALL-UNNAMED')
    sourceCompatibility = 11
    targetCompatibility = 11
}

jar {
    enabled = false
    dependsOn shadowJar
}

shadowJar {
    classifier = null


    minimize {
        exclude(dependency('com.google.code.gson:.*:.*'))
        exclude(dependency('com.github.steveice10:.*:.*'))
        exclude(dependency('libs:JFoenix:.*'))
        exclude(project(":HMCLTransformerDiscoveryService"))
    }

    manifest {
        attributes 'Created-By': 'Copyright(c) 2013-2021 huangyuhui.',
                'Main-Class': mainClassName,
                'Multi-Release': 'true',
                'Implementation-Version': project.version,
                'Microsoft-Auth-Id': microsoftAuthId,
                'Microsoft-Auth-Secret': microsoftAuthSecret,
                'Build-Channel': versionType,
                'Class-Path': 'pack200.jar',
                'Add-Opens': [
                        'java.base/java.lang',
                        'java.base/java.lang.reflect',
                        'javafx.graphics/javafx.css',
                        'javafx.base/com.sun.javafx.runtime',
                        'javafx.controls/com.sun.javafx.scene.control.behavior',
                        'javafx.controls/javafx.scene.control.skin',
                        'javafx.controls/com.sun.javafx.scene.control',
                        'javafx.base/com.sun.javafx.binding',
                        'javafx.base/com.sun.javafx.event',
                        'javafx.graphics/com.sun.javafx.stage'
                ].join(" "),
                'Add-Exports': [
                        'java.base/jdk.internal.loader',
                        'javafx.controls/com.sun.javafx.scene.control.behavior',
                        'javafx.controls/javafx.scene.control.skin',
                        'javafx.controls/com.sun.javafx.scene.control',
                        'javafx.base/com.sun.javafx.binding',
                        'javafx.graphics/com.sun.javafx.stage',
                        'javafx.base/com.sun.javafx.event'
                ].join(" ")
    }

    doLast {
        repack(jar.archivePath) // see repack()
        attachSignature(jar.archivePath)
        createChecksum(jar.archivePath)
    }
}

def createExecutable(String suffix, String header) {
    def output = new File(jar.archivePath.parentFile, jar.archivePath.name[0..-4] + suffix)
    output.bytes = new File(project.projectDir, header).bytes
    output << jar.archivePath.bytes
    createChecksum(output)
}

processResources {
    ext.convertToBSS = { String resource ->
        // exclude resource
        doFirst {
            def cssFile = new File(this.projectDir, "src/main/resources/" + resource)
            def bssFile = new File(this.projectDir, "build/compiled-resources/" + resource[0..-4] + "bss")
            bssFile.parentFile.mkdirs()
            javaexec {
                classpath = sourceSets.main.compileClasspath
                mainClass = "com.sun.javafx.css.parser.Css2Bin"
                args = [cssFile, bssFile]
            }
        }
    }
    from "build/compiled-resources"

    convertToBSS "assets/css/root.css"
    convertToBSS "assets/css/blue.css"

    into('META-INF/versions/11') {
        from sourceSets.java11.output
    }
    dependsOn java11Classes
}

task makePack(dependsOn: jar) {
    ext.outputPath = new File(jar.archivePath.parentFile, jar.archivePath.name[0..-4] + "pack")
    doLast {
        outputPath.newOutputStream().withCloseable { out ->
            new JarFile(jar.archivePath).withCloseable { jarFile -> packer.pack(jarFile, out) }
        }
        createChecksum(outputPath)
    }
}

task makePackXz(dependsOn: makePack) doLast {
    def packXz = new File(makePack.outputPath.parentFile, makePack.outputPath.name + ".xz")
    // Our CI server does not have enough memory space to compress file at highest level.
    new XZOutputStream(packXz.newOutputStream(), new LZMA2Options(5)).withCloseable { it << makePack.outputPath.bytes }
    createChecksum(packXz)
}

task makePackGz(dependsOn: makePack) doLast {
    def packGz = new File(makePack.outputPath.parentFile, makePack.outputPath.name + ".gz")
    new GZIPOutputStream(packGz.newOutputStream()).withCloseable { it << makePack.outputPath.bytes }
    createChecksum(packGz)
}


task makeExecutables(dependsOn: jar) doLast {
    createExecutable("exe", "src/main/resources/assets/HMCLauncher.exe")
}

build.dependsOn makePackXz
build.dependsOn makePackGz
build.dependsOn makeExecutables
