buildscript {
    repositories {
        mavenCentral()
        maven { url = "https://maven.minecraftforge.net" }
        maven { url = 'https://maven.parchmentmc.org' }
    }
    dependencies {
        classpath 'net.minecraftforge.gradle:ForgeGradle:5.1.+'
        classpath "org.spongepowered:mixingradle:0.7.+"
        classpath 'org.parchmentmc:librarian:1.+'
    }
}

plugins {
    id "checkstyle"
    id "jacoco"
    id "maven-publish"
    id "com.github.hierynomus.license" version "0.16.1"
    id "com.matthewprenger.cursegradle" version "1.4.0"
    id "com.github.breadmoirai.github-release" version "2.2.12"
    id "org.jetbrains.kotlin.jvm" version "1.6.0"
    id "com.modrinth.minotaur" version "1.2.1"
}

apply plugin: 'net.minecraftforge.gradle'
apply plugin: "org.spongepowered.mixin"
apply plugin: 'org.parchmentmc.librarian.forgegradle'

version = mod_version

group = "org.squiddev"
archivesBaseName = "cc-tweaked-${mc_version}"

def javaVersion = JavaLanguageVersion.of(8)
java {
    toolchain {
        languageVersion = javaVersion
    }

    withSourcesJar()
    withJavadocJar()
}

tasks.withType(JavaExec).configureEach {
    javaLauncher = javaToolchains.launcherFor {
        languageVersion = javaVersion
    }
}

sourceSets {
    main.resources {
        srcDir 'src/generated/resources'
    }

    testMod {}
}

minecraft {
    runs {
        all {
            property 'forge.logging.markers', 'REGISTRIES'
            property 'forge.logging.console.level', 'debug'

            mods {
                computercraft {
                    source sourceSets.main
                }
            }

            arg "-mixin.config=computercraft.mixins.json"
        }

        client {
            workingDirectory project.file('run')
        }

        server {
            workingDirectory project.file("run/server")
            arg "--nogui"
        }

        data {
            workingDirectory project.file('run')
            args '--mod', 'computercraft', '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/')
        }

        testClient {
            workingDirectory project.file('test-files/client')
            parent runs.client

            mods {
                cctest {
                    source sourceSets.testMod
                }
            }
        }

        testServer {
            workingDirectory project.file('test-files/server')
            parent runs.server

            mods {
                cctest {
                    source sourceSets.testMod
                }
            }
        }
    }

    mappings channel: 'parchment', version: "${mapping_version}-${mc_version}"

    accessTransformer file('src/main/resources/META-INF/accesstransformer.cfg')
    accessTransformer file('src/testMod/resources/META-INF/accesstransformer.cfg')
}

mixin {
    add sourceSets.main, 'computercraft.mixins.refmap.json'
}

repositories {
    mavenCentral()
    maven {
        name "SquidDev"
        url "https://squiddev.cc/maven"
    }
}

configurations {
    shade
    implementation.extendsFrom shade
    cctJavadoc

    testModImplementation.extendsFrom(implementation)
    testModImplementation.extendsFrom(testImplementation)
}

dependencies {
    checkstyle "com.puppycrawl.tools:checkstyle:8.25"

    minecraft "net.minecraftforge:forge:${mc_version}-${forge_version}"
    annotationProcessor 'org.spongepowered:mixin:0.8.4:processor'

    compileOnly fg.deobf("mezz.jei:jei-1.16.5:7.7.0.104:api")
    compileOnly fg.deobf("com.blamejared.crafttweaker:CraftTweaker-1.16.5:7.1.0.313")
    compileOnly fg.deobf("commoble.morered:morered-1.16.5:2.1.1.0")

    runtimeOnly fg.deobf("mezz.jei:jei-1.16.5:7.7.0.104")

    shade 'org.squiddev:Cobalt:0.5.5'

    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
    testImplementation 'org.junit.jupiter:junit-jupiter-params:5.7.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
    testImplementation 'org.hamcrest:hamcrest:2.2'
    testImplementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.0'
    testImplementation 'org.jetbrains.kotlin:kotlin-reflect:1.6.0'
    testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'

    testModImplementation sourceSets.main.output

    cctJavadoc 'cc.tweaked:cct-javadoc:1.4.5'
}

// Compile tasks

compileTestModJava {
    dependsOn(compileJava)
}

javadoc {
    include "dan200/computercraft/api/**/*.java"
}

task luaJavadoc(type: Javadoc) {
    description "Generates documentation for Java-side Lua functions."
    group "documentation"

    source = sourceSets.main.allJava
    destinationDir = file("${project.docsDir}/luaJavadoc")
    classpath = sourceSets.main.compileClasspath

    options.docletpath = configurations.cctJavadoc.files as List
    options.doclet = "cc.tweaked.javadoc.LuaDoclet"
    options.noTimestamp = false

    javadocTool = javaToolchains.javadocToolFor {
        languageVersion = JavaLanguageVersion.of(11)
    }
}

jar {
    manifest {
        attributes([
            "Specification-Title"     : "computercraft",
            "Specification-Vendor"    : "SquidDev",
            "Specification-Version"   : "1",
            "Implementation-Title"    : "CC: Tweaked",
            "Implementation-Version"  : "${mod_version}",
            "Implementation-Vendor"   : "SquidDev",
            "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"),
            "MixinConfigs"            : "computercraft.mixins.json",
        ])
    }

    from configurations.shade.collect { it.isDirectory() ? it : zipTree(it) }
}

[compileJava, compileTestJava, compileTestModJava].forEach {
    it.configure {
        options.compilerArgs << "-Xlint" << "-Xlint:-processing"
    }
}

processResources {
    inputs.property "version", mod_version
    inputs.property "mcversion", mc_version

    def hash = 'none'
    Set<String> contributors = []
    try {
        hash = ["git", "-C", projectDir, "rev-parse", "HEAD"].execute().text.trim()

        def blacklist = ['GitHub', 'dan200', 'Daniel Ratcliffe']
        ["git", "-C", projectDir, "log", "--format=tformat:%an%n%cn"].execute().text.split('\n').each {
            if (!blacklist.contains(it)) contributors.add(it)
        }
    } catch (Exception e) {
        e.printStackTrace()
    }
    inputs.property "commithash", hash
    duplicatesStrategy = DuplicatesStrategy.INCLUDE

    from(sourceSets.main.resources.srcDirs) {
        include 'META-INF/mods.toml'
        include 'data/computercraft/lua/rom/help/credits.txt'

        expand 'version': mod_version,
                'mcversion': mc_version,
                'gitcontributors': contributors.sort(false, String.CASE_INSENSITIVE_ORDER).join('\n')
    }

    from(sourceSets.main.resources.srcDirs) {
        exclude 'META-INF/mods.toml'
        exclude 'data/computercraft/lua/rom/help/credits.txt'
    }
}

sourcesJar {
    duplicatesStrategy = DuplicatesStrategy.INCLUDE
}

// Web tasks


import com.hierynomus.gradle.license.tasks.LicenseCheck
import com.hierynomus.gradle.license.tasks.LicenseFormat
import org.apache.tools.ant.taskdefs.condition.Os

List<String> mkCommand(String command) {
    return Os.isFamily(Os.FAMILY_WINDOWS) ? ["cmd", "/c", command] : ["sh", "-c", command]
}

task rollup(type: Exec) {
    group = "build"
    description = "Bundles JS into rollup"

    inputs.files(fileTree("src/web")).withPropertyName("sources")
    inputs.file("package-lock.json").withPropertyName("package-lock.json")
    inputs.file("tsconfig.json").withPropertyName("Typescript config")
    inputs.file("rollup.config.js").withPropertyName("Rollup config")
    outputs.file("$buildDir/rollup/index.js").withPropertyName("output")

    commandLine mkCommand('"node_modules/.bin/rollup" --config rollup.config.js')
}

task illuaminateDocs(type: Exec, dependsOn: [rollup, luaJavadoc]) {
    group = "build"
    description = "Bundles JS into rollup"

    inputs.files(fileTree("doc")).withPropertyName("docs")
    inputs.files(fileTree("src/main/resources/data/computercraft/lua/rom")).withPropertyName("lua rom")
    inputs.file("illuaminate.sexp").withPropertyName("illuaminate.sexp")
    inputs.dir("$buildDir/docs/luaJavadoc")
    inputs.file("$buildDir/rollup/index.js").withPropertyName("scripts")
    inputs.file("src/web/styles.css").withPropertyName("styles")
    outputs.dir("$buildDir/docs/lua")

    commandLine mkCommand('"bin/illuaminate" doc-gen')
}

task docWebsite(type: Copy, dependsOn: [illuaminateDocs]) {
    from('doc') {
        include 'logo.png'
        include 'images/**'
    }
    from("$buildDir/rollup") {
        exclude 'index.js'
    }
    into "${project.docsDir}/lua"
}

// Check tasks

test {
    useJUnitPlatform()
    testLogging {
        events "skipped", "failed"
    }
}

jacocoTestReport {
    dependsOn('test')
    reports {
        xml.required = true
        html.required = true
    }
}

check.dependsOn jacocoTestReport

license {
    mapping("java", "SLASHSTAR_STYLE")
    strictCheck true

    ext.year = Calendar.getInstance().get(Calendar.YEAR)
}

[licenseMain, licenseFormatMain].forEach {
    it.configure {
        include("**/*.java")
        exclude("dan200/computercraft/api/**")
        header file('config/license/main.txt')
    }
}

[licenseTest, licenseFormatTest, licenseTestMod, licenseFormatTestMod].forEach {
    it.configure {
        include("**/*.java")
        header file('config/license/main.txt')
    }
}

gradle.projectsEvaluated {
    tasks.withType(LicenseFormat) {
        outputs.upToDateWhen { false }
    }
}


task licenseAPI(type: LicenseCheck)
task licenseFormatAPI(type: LicenseFormat)
[licenseAPI, licenseFormatAPI].forEach {
    it.configure {
        source = sourceSets.main.java
        include("dan200/computercraft/api/**")
        header file('config/license/api.txt')
    }
}

task setupServer(type: Copy) {
    group "test server"
    description "Sets up the environment for the test server."

    from("src/testMod/server-files") {
        include "eula.txt"
        include "server.properties"
    }
    into "test-files/server"
}

["Client", "Server"].forEach { name ->
    tasks.register("test$name", JavaExec.class).configure {
        it.group('In-game tests')
        it.description("Runs tests on a temporary Minecraft instance.")
        it.dependsOn(setupServer, "prepareRunTest$name", "cleanTest$name", 'compileTestModJava')

        // Copy from runTestServer. We do it in this slightly odd way as runTestServer
        // isn't created until the task is configured (which is no good for us).
        JavaExec exec = tasks.getByName("runTest$name")
        exec.copyTo(it)
        it.setClasspath(exec.getClasspath())
        it.mainClass = exec.mainClass
        it.setArgs(exec.getArgs())

        it.systemProperty('forge.logging.console.level', 'info')
        it.systemProperty('cctest.run', 'true')

        // Jacoco and modlauncher don't play well together as the classes loaded in-game don't
        // match up with those written to disk. We get Jacoco to dump all classes to disk, and
        // use that when generating the report.
        def coverageOut = new File(buildDir, "jacocoClassDump/test$name")
        jacoco.applyTo(it)
        it.jacoco.setIncludes(["dan200.computercraft.*"])
        it.jacoco.setClassDumpDir(coverageOut)
        it.outputs.dir(coverageOut)
        // Older versions of modlauncher don't include a protection domain (and thus no code
        // source). Jacoco skips such classes by default, so we need to explicitly include them.
        it.jacoco.setIncludeNoLocationClasses(true)
    }

    tasks.register("jacocoTest${name}Report", JacocoReport.class).configure {
        it.group('In-game')
        it.description("Generate coverage reports for test$name")
        it.dependsOn("test$name")

        it.executionData(new File(buildDir, "jacoco/test${name}.exec"))
        it.sourceDirectories.from(sourceSets.main.allJava.srcDirs)
        it.classDirectories.from(new File(buildDir, "jacocoClassDump/test$name"))

        it.reports {
            xml.enabled true
            html.enabled true
        }
    }

    if (name != "Client" || project.findProperty('cc.tweaked.clientTests') == 'true') {
        // Don't run client tests unless explicitly opted into them. They're a bit of a faff
        // to run and pretty flakey.
        check.dependsOn("jacocoTest${name}Report")
    }
}


// Upload tasks

task checkRelease {
    group "upload"
    description "Verifies that everything is ready for a release"

    inputs.property "version", mod_version
    inputs.file("src/main/resources/data/computercraft/lua/rom/help/changelog.md")
    inputs.file("src/main/resources/data/computercraft/lua/rom/help/whatsnew.md")

    doLast {
        def ok = true

        // Check we're targetting the current version
        def whatsnew = new File(projectDir, "src/main/resources/data/computercraft/lua/rom/help/whatsnew.md").readLines()
        if (whatsnew[0] != "New features in CC: Tweaked $mod_version") {
            ok = false
            project.logger.error("Expected `whatsnew.md' to target $mod_version.")
        }

        // Check "read more" exists and trim it
        def idx = whatsnew.findIndexOf { it == 'Type "help changelog" to see the full version history.' }
        if (idx == -1) {
            ok = false
            project.logger.error("Must mention the changelog in whatsnew.md")
        } else {
            whatsnew = whatsnew.getAt(0..<idx)
        }

        // Check whatsnew and changelog match.
        def versionChangelog = "# " + whatsnew.join("\n")
        def changelog = new File(projectDir, "src/main/resources/data/computercraft/lua/rom/help/changelog.md").getText()
        if (!changelog.startsWith(versionChangelog)) {
            ok = false
            project.logger.error("whatsnew and changelog are not in sync")
        }

        if (!ok) throw new IllegalStateException("Could not check release")
    }
}
check.dependsOn checkRelease

curseforge {
    apiKey = project.hasProperty('curseForgeApiKey') ? project.curseForgeApiKey : ''
    project {
        id = '282001'
        releaseType = 'release'
        changelog = "Release notes can be found on the GitHub repository (https://github.com/cc-tweaked/CC-Tweaked/releases/tag/v${mc_version}-${mod_version})."

        addGameVersion "${mc_version}"
    }
}

import com.modrinth.minotaur.TaskModrinthUpload
tasks.register('publishModrinth', TaskModrinthUpload.class).configure {
    dependsOn('assemble', 'reobfJar')
    onlyIf {
        project.hasProperty('modrinthApiKey')
    }

    token = project.hasProperty('modrinthApiKey') ? project.getProperty('modrinthApiKey') : ''
    projectId = 'gu7yAYhd'
    versionNumber = "${project.mc_version}-${project.mod_version}"
    uploadFile = jar
    addGameVersion(project.mc_version)
    changelog = "Release notes can be found on the [GitHub repository](https://github.com/cc-tweaked/CC-Tweaked/releases/tag/v${mc_version}-${mod_version})."
    addLoader('forge')
}

tasks.withType(GenerateModuleMetadata) {
    // We can't generate metadata as that includes Forge as a dependency.
    enabled = false
}

publishing {
    publications {
        maven(MavenPublication) {
            from components.java

            pom {
                name = 'CC: Tweaked'
                description = 'CC: Tweaked is a fork of ComputerCraft, adding programmable computers, turtles and more to Minecraft.'
                url = 'https://github.com/cc-tweaked/CC-Tweaked'

                scm {
                    url = 'https://github.com/cc-tweaked/CC-Tweaked.git'
                }

                issueManagement {
                    system = 'github'
                    url = 'https://github.com/cc-tweaked/CC-Tweaked/issues'
                }

                licenses {
                    license {
                        name = 'ComputerCraft Public License, Version 1.0'
                        url = 'https://github.com/cc-tweaked/CC-Tweaked/blob/mc-1.15.x/LICENSE'
                    }
                }

                withXml { asNode().remove(asNode().get("dependencies")) }
            }
        }
    }

    repositories {
        if (project.hasProperty("mavenUser")) {
            maven {
                name = "SquidDev"
                url = "https://squiddev.cc/maven"
                credentials {
                    username = project.property("mavenUser") as String
                    password = project.property("mavenPass") as String
                }
            }
        }
    }
}

githubRelease {
    token project.hasProperty('githubApiKey') ? project.githubApiKey : ''
    owner 'cc-tweaked'
    repo 'CC-Tweaked'
    targetCommitish.set(project.provider({
        try {
            return ["git", "-C", projectDir, "rev-parse", "--abbrev-ref", "HEAD"].execute().text.trim()
        } catch (Exception e) {
            e.printStackTrace()
        }
        return "master"
    }))

    tagName "v${mc_version}-${mod_version}"
    releaseName "[${mc_version}] ${mod_version}"
    body.set(project.provider({
        "## " + new File(projectDir, "src/main/resources/data/computercraft/lua/rom/help/whatsnew.md")
            .readLines()
            .takeWhile { it != 'Type "help changelog" to see the full version history.' }
            .join("\n").trim()
    }))
    prerelease false
}

def uploadTasks = ["publish", "curseforge", "publishModrinth", "githubRelease"]
uploadTasks.forEach { tasks.getByName(it).dependsOn checkRelease }

task uploadAll(dependsOn: uploadTasks) {
    group "upload"
    description "Uploads to all repositories (Maven, Curse, Modrinth, GitHub release)"
}
