/*
 * Copyright (c) 2021, Peter Abeles. All Rights Reserved.
 *
 * This file is part of Efficient Java Matrix Library (EJML).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import net.ltgt.gradle.errorprone.CheckSeverity
import org.gradle.api.tasks.testing.logging.TestExceptionFormat

plugins {
    id "com.peterabeles.gversion" version "1.10" apply false
    id "net.ltgt.errorprone" version "1.2.1" apply false
    id "com.diffplug.spotless" version "5.6.1" apply false
}

allprojects {
    apply plugin: 'eclipse'
    apply plugin: 'com.peterabeles.gversion'

    group = 'org.ejml'
    version = '0.41'
}

subprojects {
    apply plugin: 'java-library'
    apply plugin: 'maven-publish'
    apply plugin: 'signing'
    apply plugin: 'net.ltgt.errorprone'
    apply plugin: 'com.diffplug.spotless'

    java {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
        withJavadocJar()
        withSourcesJar()
    }

    tasks.withType(JavaCompile).all {
        options.incremental = true                    // Should make single file changes faster
        options.compilerArgs = ["--release", "8",]    // Output byte code compatible with Java 8
    }

    // Fail on jar conflict
    configurations.all { resolutionStrategy { failOnVersionConflict() } }

    // Force the release build to fail if it depends on a SNAPSHOT
    jar.dependsOn checkDependsOnSNAPSHOT

    // Force uploadArchives to fail if trying to upload a stable release and git is dirty
    uploadArchives.dependsOn failDirtyNotSnapshot

    // To make ErrorProne and Kotlin plugins happy
    configurations.all {
        resolutionStrategy {
            force 'org.jetbrains:annotations:20.0.0'
            force 'com.google.guava:guava:30.1-jre'
            force 'com.google.errorprone:error_prone_annotations:2.4.0'
            force 'com.google.code.findbugs:jsr305:3.0.2'
            force 'org.checkerframework:checker-qual:2.10.0'
        }
    }

    test {
        useJUnitPlatform()
        reports.html.enabled = false
        testLogging.showStandardStreams = true                 // Print stdout making debugging easier
        testLogging.exceptionFormat TestExceptionFormat.FULL
        testLogging.showCauses true
        testLogging.showExceptions true
        testLogging.showStackTraces true
    }

    repositories {
        mavenCentral()
        mavenLocal()

        maven { url = "https://oss.sonatype.org/content/repositories/snapshots/" }
        maven { url = 'https://jitpack.io' } // Allows annotations past Java 8 to be used
    }

    sourceSets {
        main {
            java { srcDir 'src' }
            resources { srcDir 'resources/src' }
        }

        generate { java { srcDir 'generate' } }

        benchmarks {
            java { srcDir 'benchmarks/src' }
            resources { srcDir 'benchmarks/resources' }
        }

        test {
            java { srcDir 'test' }
            resources { srcDir 'resources/test' }
        }
    }

    dependencies {
        compileOnly 'us.hebi.matlab.mat:mfl-ejml:0.5.7'
        compileOnly 'org.projectlombok:lombok:1.18.10'
        compileOnly 'org.jetbrains:annotations:20.0.0' // @Nullable
        compileOnly 'javax.annotation:jsr250-api:1.0'  // @Generated
        testCompileOnly project.sourceSets.main.compileClasspath

        testImplementation( 'org.junit.jupiter:junit-jupiter-api:5.4.0' )
        testImplementation( 'org.junit.jupiter:junit-jupiter-params:5.4.0' )
        testRuntimeOnly( 'org.junit.jupiter:junit-jupiter-engine:5.4.0' )

        generateCompile project(':main:autocode')

        // needed to use Java 11+ syntax with Java 1.8 byte code
        annotationProcessor('com.github.bsideup.jabel:jabel-javac-plugin:0.2.0') {
            exclude group: 'net.bytebuddy', module: 'byte-buddy'
            exclude group: 'net.bytebuddy', module: 'byte-buddy-agent'
        }

        annotationProcessor 'org.projectlombok:lombok:1.18.10'  // @Getter @Setter
        annotationProcessor("net.bytebuddy:byte-buddy:1.10.10")
        annotationProcessor("net.bytebuddy:byte-buddy-agent:1.10.10")

        errorprone "com.google.errorprone:error_prone_core:2.5.1"   // adds benchmarks/generated to sourceSet
        errorproneJavac "com.google.errorprone:javac:9+181-r4173-1" // annoying...

        benchmarksImplementation project.sourceSets.main.runtimeClasspath
        benchmarksImplementation project.sourceSets.main.compileClasspath
        ['1.27'].each { String a->
            benchmarksCompile('org.openjdk.jmh:jmh-core:'+a)
            benchmarksAnnotationProcessor 'org.openjdk.jmh:jmh-generator-annprocess:'+a
        }

        // even if it's not used you still need to include the dependency
        annotationProcessor "com.uber.nullaway:nullaway:0.8.0"
        testAnnotationProcessor "com.uber.nullaway:nullaway:0.8.0"
        benchmarksAnnotationProcessor "com.uber.nullaway:nullaway:0.8.0"
        generateAnnotationProcessor "com.uber.nullaway:nullaway:0.8.0"
    }

    javadoc {
        configure(options) {
            links = ['http://docs.oracle.com/javase/8/docs/api/']
            failOnError = false
            enabled = !project.version.contains("SNAPSHOT") // disable to stop it from spamming stdout
        }
    }

    // InconsistentCapitalization is disabled because in Math capital letters are often used for matrices and lower case
    // for vectors or scalars. Perhaps a more verbose name should be used but it's disabled for now to reduce build spam
    tasks.withType(JavaCompile).configureEach {
        options.errorprone.enabled = false
        if( path.contains("Benchmarks") || path.contains("examples") || path.contains("regression"))
            return

        options.errorprone.enabled = true
        options.errorprone.disableWarningsInGeneratedCode = true
        options.errorprone.disable("TypeParameterUnusedInFormals","StringSplitter","InconsistentCapitalization",
                "HidingField", // this is sometimes done when the specific type is known by child. Clean up later.
                "ClassNewInstance", // yes it's deprecated, but new version is more verbose with ignored errors
                "FloatingPointLiteralPrecision", // too many false positives in test code
                "MissingSummary","UnescapedEntity","EmptyBlockTag")
        options.errorprone.error("MissingOverride","MissingCasesInEnumSwitch","BadInstanceof",
                "PublicConstructorForAbstractClass","EmptyCatch","NarrowingCompoundAssignment","JdkObsolete")

        if( name.startsWith("compileTest") ) {
            options.errorprone.disable("ReferenceEquality","IntLongMath","ClassCanBeStatic")
        }

        options.errorprone {
            check("NullAway", CheckSeverity.ERROR)
            option("NullAway:TreatGeneratedAsUnannotated", true)
            option("NullAway:AnnotatedPackages", "org.ejml")
        }
    }

    // Skip these codeless directories when publishing jars locally or to a remote destination
    if (['main','examples','main:autocode','regression'].contains(name)) {
        project.jar.enabled = false
        project.uploadArchives.enabled = false
        project.tasks.publish.enabled = false
    }

    spotless {
        ratchetFrom 'origin/SNAPSHOT'

        format 'misc', {
            // define the files to apply `misc` to
            target '*.gradle', '*.md', '.gitignore'

            // define the steps to apply to those files
            trimTrailingWhitespace()
            indentWithTabs()
            endWithNewline()
        }
        java {
            // There is no good way to ignore auto generated code since you can't check to see if it's in git or not
            // there is no simple regex pattern. To apply spotless first remove all auto generated code.
            target('**/ejml-core/src/org/ejml/**/*.java',
                    '**/ejml-ddense/src/org/ejml/**/*.java',
                    '**/ejml-dsparse/src/org/ejml/**/*.java',
                    '**/ejml-kotlin/src/org/ejml/**/*.java',
                    '**/ejml-simple/src/org/ejml/**/*.java',
                    '**/ejml-zdense/src/**/*.java')

            toggleOffOn('formatter:off', 'formatter:on')
            removeUnusedImports()
            endWithNewline()

            licenseHeaderFile "${project.rootDir}/docs/copyright.txt", 'package '
        }
    }

    if (!project.tasks.publish.enabled)
        return

    // if Maven central isn't setup in ~/.gradle/gradle.properties fill in these variables to make it happy
    if( !project.hasProperty('ossrhUsername') ) {
        ext.ossrhUsername = "dummy"
        ext.ossrhPassword = "dummy"
    }

    publishing {
        publications {
            mavenJava(MavenPublication) {
                from components.java
                pom {
                    name = 'EJML'
                    description = 'A fast and easy to use dense and sparse matrix linear algebra library written in Java.'
                    url = 'http://ejml.org/'

                    licenses {
                        license {
                            name = 'The Apache License, Version 2.0'
                            url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
                        }
                    }
                    developers {
                        developer {
                            id = 'pabeles'
                            name = 'Peter Abeles'
                            email = 'peter.abeles@gmail.com'
                        }
                    }
                    scm {
                        connection = 'scm:git:git://github.com/lessthanoptimal/ejml.git'
                        developerConnection = 'scm:git:git://github.com/lessthanoptimal/ejml.git'
                        url = 'https://github.com/lessthanoptimal/ejml'
                    }
                }
            }
        }

        repositories {
            maven {
                def releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
                def snapshotsRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots/"
                url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
                credentials {
                    username ossrhUsername
                    password ossrhPassword
                }
            }
        }
    }

    if (ext.ossrhPassword != "dummy") {
        signing { sign publishing.publications.mavenJava }
    }
}

def allModules = [
        ':main:ejml-core',
        ':main:ejml-cdense',
        ':main:ejml-ddense',
        ':main:ejml-dsparse',
        ':main:ejml-dsparse',
        ':main:ejml-fdense',
        ':main:ejml-zdense',
        ':main:ejml-simple',
        ':main:ejml-experimental',
]

// Creates a directory with all the compiled jars
task createLibraryDirectory( dependsOn: allModules.collect{ it+":jar"}+allModules.collect{ it+":sourcesJar"}) {
    doLast {
        // Create lists of .class jars and source jars
        ext.listJars = files(allModules.collect{ project(it).tasks.jar.archivePath })
        ext.listSource = files(allModules.collect{ project(it).tasks.sourcesJar.archivePath })

        file('libraries').deleteDir()
        file('libraries').mkdir()

        copy {
            from ext.listJars
            from ext.listSource
            into 'libraries'
        }
    }
}

def javadocProjects = [
        ':main:ejml-core',
        ':main:ejml-ddense',
        ':main:ejml-dsparse',
        ':main:ejml-fdense',
        ':main:ejml-fsparse',
        ':main:ejml-zdense',
        ':main:ejml-cdense',
        ':main:ejml-simple'
]

task alljavadoc(type: Javadoc) {
    // only include source code in src directory to avoid including 3rd party code which some projects do as a hack
    source = javadocProjects.collect { project(it).fileTree('src').include('**/*.java') }
//    source = javadocProjects.collect { project(it).sourceSets.main.allJava }
    classpath = files(javadocProjects.collect { project(it).sourceSets.main.compileClasspath })

    destinationDir = file("${buildDir}/docs/javadoc")

    // Hack for Java 8u121 and beyond. Comment out if running an earlier version of Java
    options.addBooleanOption("-allow-script-in-comments", true)

    // Add a list of uses of a class to javadoc
    options.use = true

    configure(options) {
        failOnError = false
        docTitle = "Efficient Java Matrix Library (EJML) v$project.version"
        links = [ 'http://docs.oracle.com/javase/8/docs/api/' ]
        bottom = file('docs/bottom.txt').text
    }
}

task oneJarBin(type: Jar, dependsOn: javadocProjects.collect { it + ":compileJava" }) {
    archiveFile.set(file("ejml-v${project.version}.jar"))

    from files(javadocProjects.collect { project(it).sourceSets.main.output.classesDirs }) {
        exclude 'META-INF/*.RSA', 'META-INF/*.SF', 'META-INF/*.DSA'
    }
}

wrapper {
    distributionType = Wrapper.DistributionType.BIN
    gradleVersion = '6.4.1'
}

